blob: fd0bb8950c6eebfdef3532224375acfb16f69b4d [file] [log] [blame]
John Kessenich140f3df2015-06-26 16:58:36 -06001//
John Kessenich927608b2017-01-06 12:34:14 -07002// Copyright (C) 2015 LunarG, Inc.
John Kessenich140f3df2015-06-26 16:58:36 -06003//
John Kessenich927608b2017-01-06 12:34:14 -07004// All rights reserved.
John Kessenich140f3df2015-06-26 16:58:36 -06005//
John Kessenich927608b2017-01-06 12:34:14 -07006// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions
8// are met:
John Kessenich140f3df2015-06-26 16:58:36 -06009//
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//
John Kessenich927608b2017-01-06 12:34:14 -070022// 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.
John Kessenich140f3df2015-06-26 16:58:36 -060034//
35
36#include "SPVRemapper.h"
37#include "doc.h"
38
39#if !defined (use_cpp11)
40// ... not supported before C++11
41#else // defined (use_cpp11)
42
43#include <algorithm>
44#include <cassert>
Andrew Woloszyn8b64fa52015-08-17 11:39:38 -040045#include "../glslang/Include/Common.h"
John Kessenich140f3df2015-06-26 16:58:36 -060046
47namespace spv {
48
49 // By default, just abort on error. Can be overridden via RegisterErrorHandler
50 spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); };
51 // By default, eat log messages. Can be overridden via RegisterLogHandler
52 spirvbin_t::logfn_t spirvbin_t::logHandler = [](const std::string&) { };
53
54 // This can be overridden to provide other message behavior if needed
55 void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const
56 {
57 if (verbose >= minVerbosity)
58 logHandler(std::string(indent, ' ') + txt);
59 }
60
61 // hash opcode, with special handling for OpExtInst
62 std::uint32_t spirvbin_t::asOpCodeHash(unsigned word)
63 {
64 const spv::Op opCode = asOpCode(word);
65
66 std::uint32_t offset = 0;
67
68 switch (opCode) {
69 case spv::OpExtInst:
70 offset += asId(word + 4); break;
71 default:
72 break;
73 }
74
75 return opCode * 19 + offset; // 19 = small prime
76 }
77
78 spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const
79 {
80 static const int maxCount = 1<<30;
81
82 switch (opCode) {
83 case spv::OpTypeFloat: // fall through...
84 case spv::OpTypePointer: return range_t(2, 3);
85 case spv::OpTypeInt: return range_t(2, 4);
John Kessenich5e4b1242015-08-06 22:53:06 -060086 // TODO: case spv::OpTypeImage:
87 // TODO: case spv::OpTypeSampledImage:
John Kessenich140f3df2015-06-26 16:58:36 -060088 case spv::OpTypeSampler: return range_t(3, 8);
89 case spv::OpTypeVector: // fall through
90 case spv::OpTypeMatrix: // ...
91 case spv::OpTypePipe: return range_t(3, 4);
92 case spv::OpConstant: return range_t(3, maxCount);
93 default: return range_t(0, 0);
94 }
95 }
96
97 spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const
98 {
99 static const int maxCount = 1<<30;
100
101 if (isConstOp(opCode))
102 return range_t(1, 2);
103
104 switch (opCode) {
105 case spv::OpTypeVector: // fall through
John Kessenichecba76f2017-01-06 00:34:48 -0700106 case spv::OpTypeMatrix: // ...
107 case spv::OpTypeSampler: // ...
108 case spv::OpTypeArray: // ...
109 case spv::OpTypeRuntimeArray: // ...
John Kessenich140f3df2015-06-26 16:58:36 -0600110 case spv::OpTypePipe: return range_t(2, 3);
111 case spv::OpTypeStruct: // fall through
112 case spv::OpTypeFunction: return range_t(2, maxCount);
113 case spv::OpTypePointer: return range_t(3, 4);
114 default: return range_t(0, 0);
115 }
116 }
117
118 spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const
119 {
120 static const int maxCount = 1<<30;
121
122 switch (opCode) {
123 case spv::OpTypeArray: // fall through...
124 case spv::OpTypeRuntimeArray: return range_t(3, 4);
125 case spv::OpConstantComposite: return range_t(3, maxCount);
126 default: return range_t(0, 0);
127 }
128 }
129
steve-lunarg811d9f42016-08-17 08:33:49 -0600130 // Return the size of a type in 32-bit words. This currently only
131 // handles ints and floats, and is only invoked by queries which must be
132 // integer types. If ever needed, it can be generalized.
133 unsigned spirvbin_t::typeSizeInWords(spv::Id id) const
134 {
135 const unsigned typeStart = idPos(id);
136 const spv::Op opCode = asOpCode(typeStart);
137
LoopDawg8004d362017-09-17 10:38:52 -0600138 if (errorLatch)
139 return 0;
140
steve-lunarg811d9f42016-08-17 08:33:49 -0600141 switch (opCode) {
142 case spv::OpTypeInt: // fall through...
143 case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32;
144 default:
145 return 0;
146 }
147 }
148
149 // Looks up the type of a given const or variable ID, and
150 // returns its size in 32-bit words.
151 unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const
152 {
153 const auto tid_it = idTypeSizeMap.find(id);
LoopDawg8004d362017-09-17 10:38:52 -0600154 if (tid_it == idTypeSizeMap.end()) {
steve-lunarg811d9f42016-08-17 08:33:49 -0600155 error("type size for ID not found");
LoopDawg8004d362017-09-17 10:38:52 -0600156 return 0;
157 }
steve-lunarg811d9f42016-08-17 08:33:49 -0600158
159 return tid_it->second;
160 }
161
John Kessenich140f3df2015-06-26 16:58:36 -0600162 // Is this an opcode we should remove when using --strip?
163 bool spirvbin_t::isStripOp(spv::Op opCode) const
164 {
165 switch (opCode) {
166 case spv::OpSource:
167 case spv::OpSourceExtension:
168 case spv::OpName:
169 case spv::OpMemberName:
170 case spv::OpLine: return true;
171 default: return false;
172 }
173 }
174
steve-lunarg811d9f42016-08-17 08:33:49 -0600175 // Return true if this opcode is flow control
GregF8548bab2016-02-01 16:44:57 -0700176 bool spirvbin_t::isFlowCtrl(spv::Op opCode) const
John Kessenich140f3df2015-06-26 16:58:36 -0600177 {
178 switch (opCode) {
179 case spv::OpBranchConditional:
GregF8548bab2016-02-01 16:44:57 -0700180 case spv::OpBranch:
181 case spv::OpSwitch:
John Kessenich140f3df2015-06-26 16:58:36 -0600182 case spv::OpLoopMerge:
GregF8548bab2016-02-01 16:44:57 -0700183 case spv::OpSelectionMerge:
184 case spv::OpLabel:
185 case spv::OpFunction:
186 case spv::OpFunctionEnd: return true;
John Kessenich140f3df2015-06-26 16:58:36 -0600187 default: return false;
188 }
189 }
190
steve-lunarg811d9f42016-08-17 08:33:49 -0600191 // Return true if this opcode defines a type
John Kessenich140f3df2015-06-26 16:58:36 -0600192 bool spirvbin_t::isTypeOp(spv::Op opCode) const
193 {
194 switch (opCode) {
195 case spv::OpTypeVoid:
196 case spv::OpTypeBool:
197 case spv::OpTypeInt:
198 case spv::OpTypeFloat:
199 case spv::OpTypeVector:
200 case spv::OpTypeMatrix:
John Kessenich5e4b1242015-08-06 22:53:06 -0600201 case spv::OpTypeImage:
John Kessenich140f3df2015-06-26 16:58:36 -0600202 case spv::OpTypeSampler:
John Kessenich140f3df2015-06-26 16:58:36 -0600203 case spv::OpTypeArray:
204 case spv::OpTypeRuntimeArray:
205 case spv::OpTypeStruct:
206 case spv::OpTypeOpaque:
207 case spv::OpTypePointer:
208 case spv::OpTypeFunction:
209 case spv::OpTypeEvent:
210 case spv::OpTypeDeviceEvent:
211 case spv::OpTypeReserveId:
212 case spv::OpTypeQueue:
Steve3573f2e2015-08-11 09:20:14 -0600213 case spv::OpTypeSampledImage:
John Kessenich140f3df2015-06-26 16:58:36 -0600214 case spv::OpTypePipe: return true;
215 default: return false;
216 }
217 }
218
steve-lunarg811d9f42016-08-17 08:33:49 -0600219 // Return true if this opcode defines a constant
John Kessenich140f3df2015-06-26 16:58:36 -0600220 bool spirvbin_t::isConstOp(spv::Op opCode) const
221 {
222 switch (opCode) {
John Kessenichea1ea972017-09-11 19:25:17 -0600223 case spv::OpConstantSampler:
224 error("unimplemented constant type");
225 return true;
John Kessenich140f3df2015-06-26 16:58:36 -0600226
GregFc6831d12018-06-19 13:47:50 -0600227 case spv::OpConstantNull:
John Kessenich140f3df2015-06-26 16:58:36 -0600228 case spv::OpConstantTrue:
229 case spv::OpConstantFalse:
John Kessenich140f3df2015-06-26 16:58:36 -0600230 case spv::OpConstantComposite:
John Kessenichea1ea972017-09-11 19:25:17 -0600231 case spv::OpConstant:
232 return true;
233
234 default:
235 return false;
John Kessenich140f3df2015-06-26 16:58:36 -0600236 }
237 }
238
239 const auto inst_fn_nop = [](spv::Op, unsigned) { return false; };
240 const auto op_fn_nop = [](spv::Id&) { };
241
242 // g++ doesn't like these defined in the class proper in an anonymous namespace.
243 // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why.
244 // Defining them externally seems to please both compilers, so, here they are.
245 const spv::Id spirvbin_t::unmapped = spv::Id(-10000);
246 const spv::Id spirvbin_t::unused = spv::Id(-10001);
247 const int spirvbin_t::header_size = 5;
248
249 spv::Id spirvbin_t::nextUnusedId(spv::Id id)
250 {
251 while (isNewIdMapped(id)) // search for an unused ID
252 ++id;
253
254 return id;
255 }
256
257 spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId)
258 {
John Kessenich66011cb2018-03-06 16:12:04 -0700259 //assert(id != spv::NoResult && newId != spv::NoResult);
John Kessenich140f3df2015-06-26 16:58:36 -0600260
LoopDawg8004d362017-09-17 10:38:52 -0600261 if (id > bound()) {
262 error(std::string("ID out of range: ") + std::to_string(id));
263 return spirvbin_t::unused;
264 }
265
John Kessenich140f3df2015-06-26 16:58:36 -0600266 if (id >= idMapL.size())
267 idMapL.resize(id+1, unused);
268
269 if (newId != unmapped && newId != unused) {
LoopDawg8004d362017-09-17 10:38:52 -0600270 if (isOldIdUnused(id)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600271 error(std::string("ID unused in module: ") + std::to_string(id));
LoopDawg8004d362017-09-17 10:38:52 -0600272 return spirvbin_t::unused;
273 }
John Kessenich140f3df2015-06-26 16:58:36 -0600274
LoopDawg8004d362017-09-17 10:38:52 -0600275 if (!isOldIdUnmapped(id)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600276 error(std::string("ID already mapped: ") + std::to_string(id) + " -> "
LoopDawg8004d362017-09-17 10:38:52 -0600277 + std::to_string(localId(id)));
John Kessenich140f3df2015-06-26 16:58:36 -0600278
LoopDawg8004d362017-09-17 10:38:52 -0600279 return spirvbin_t::unused;
280 }
281
282 if (isNewIdMapped(newId)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600283 error(std::string("ID already used in module: ") + std::to_string(newId));
LoopDawg8004d362017-09-17 10:38:52 -0600284 return spirvbin_t::unused;
285 }
John Kessenich140f3df2015-06-26 16:58:36 -0600286
287 msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId));
288 setMapped(newId);
289 largestNewId = std::max(largestNewId, newId);
290 }
291
292 return idMapL[id] = newId;
293 }
294
295 // Parse a literal string from the SPIR binary and return it as an std::string
296 // Due to C++11 RValue references, this doesn't copy the result string.
297 std::string spirvbin_t::literalString(unsigned word) const
298 {
299 std::string literal;
300
301 literal.reserve(16);
302
303 const char* bytes = reinterpret_cast<const char*>(spv.data() + word);
304
305 while (bytes && *bytes)
306 literal += *bytes++;
307
308 return literal;
309 }
310
John Kessenich140f3df2015-06-26 16:58:36 -0600311 void spirvbin_t::applyMap()
312 {
313 msg(3, 2, std::string("Applying map: "));
314
315 // Map local IDs through the ID map
316 process(inst_fn_nop, // ignore instructions
317 [this](spv::Id& id) {
318 id = localId(id);
LoopDawg8004d362017-09-17 10:38:52 -0600319
320 if (errorLatch)
321 return;
322
John Kessenich140f3df2015-06-26 16:58:36 -0600323 assert(id != unused && id != unmapped);
324 }
325 );
326 }
327
John Kessenich140f3df2015-06-26 16:58:36 -0600328 // Find free IDs for anything we haven't mapped
329 void spirvbin_t::mapRemainder()
330 {
331 msg(3, 2, std::string("Remapping remainder: "));
332
333 spv::Id unusedId = 1; // can't use 0: that's NoResult
334 spirword_t maxBound = 0;
335
336 for (spv::Id id = 0; id < idMapL.size(); ++id) {
337 if (isOldIdUnused(id))
338 continue;
339
340 // Find a new mapping for any used but unmapped IDs
LoopDawg8004d362017-09-17 10:38:52 -0600341 if (isOldIdUnmapped(id)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600342 localId(id, unusedId = nextUnusedId(unusedId));
LoopDawg8004d362017-09-17 10:38:52 -0600343 if (errorLatch)
344 return;
345 }
John Kessenich140f3df2015-06-26 16:58:36 -0600346
LoopDawg8004d362017-09-17 10:38:52 -0600347 if (isOldIdUnmapped(id)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600348 error(std::string("old ID not mapped: ") + std::to_string(id));
LoopDawg8004d362017-09-17 10:38:52 -0600349 return;
350 }
John Kessenich140f3df2015-06-26 16:58:36 -0600351
352 // Track max bound
353 maxBound = std::max(maxBound, localId(id) + 1);
LoopDawg8004d362017-09-17 10:38:52 -0600354
355 if (errorLatch)
356 return;
John Kessenich140f3df2015-06-26 16:58:36 -0600357 }
358
359 bound(maxBound); // reset header ID bound to as big as it now needs to be
360 }
361
steve-lunarg297754c2016-12-09 11:13:23 -0700362 // Mark debug instructions for stripping
John Kessenich140f3df2015-06-26 16:58:36 -0600363 void spirvbin_t::stripDebug()
364 {
steve-lunarg297754c2016-12-09 11:13:23 -0700365 // Strip instructions in the stripOp set: debug info.
John Kessenich140f3df2015-06-26 16:58:36 -0600366 process(
367 [&](spv::Op opCode, unsigned start) {
368 // remember opcodes we want to strip later
369 if (isStripOp(opCode))
370 stripInst(start);
371 return true;
372 },
373 op_fn_nop);
374 }
375
steve-lunarg297754c2016-12-09 11:13:23 -0700376 // Mark instructions that refer to now-removed IDs for stripping
377 void spirvbin_t::stripDeadRefs()
378 {
379 process(
380 [&](spv::Op opCode, unsigned start) {
381 // strip opcodes pointing to removed data
382 switch (opCode) {
383 case spv::OpName:
384 case spv::OpMemberName:
385 case spv::OpDecorate:
386 case spv::OpMemberDecorate:
387 if (idPosR.find(asId(start+1)) == idPosR.end())
388 stripInst(start);
389 break;
John Kessenichecba76f2017-01-06 00:34:48 -0700390 default:
steve-lunarg297754c2016-12-09 11:13:23 -0700391 break; // leave it alone
392 }
393
394 return true;
395 },
396 op_fn_nop);
397
398 strip();
399 }
400
401 // Update local maps of ID, type, etc positions
John Kessenich140f3df2015-06-26 16:58:36 -0600402 void spirvbin_t::buildLocalMaps()
403 {
404 msg(2, 2, std::string("build local maps: "));
405
406 mapped.clear();
407 idMapL.clear();
408// preserve nameMap, so we don't clear that.
409 fnPos.clear();
John Kessenich140f3df2015-06-26 16:58:36 -0600410 fnCalls.clear();
411 typeConstPos.clear();
steve-lunarg811d9f42016-08-17 08:33:49 -0600412 idPosR.clear();
John Kessenich140f3df2015-06-26 16:58:36 -0600413 entryPoint = spv::NoResult;
414 largestNewId = 0;
415
416 idMapL.resize(bound(), unused);
417
418 int fnStart = 0;
419 spv::Id fnRes = spv::NoResult;
420
421 // build local Id and name maps
422 process(
423 [&](spv::Op opCode, unsigned start) {
steve-lunarg811d9f42016-08-17 08:33:49 -0600424 unsigned word = start+1;
425 spv::Id typeId = spv::NoResult;
426
427 if (spv::InstructionDesc[opCode].hasType())
428 typeId = asId(word++);
429
430 // If there's a result ID, remember the size of its type
431 if (spv::InstructionDesc[opCode].hasResult()) {
432 const spv::Id resultId = asId(word++);
433 idPosR[resultId] = start;
John Kessenichecba76f2017-01-06 00:34:48 -0700434
steve-lunarg811d9f42016-08-17 08:33:49 -0600435 if (typeId != spv::NoResult) {
436 const unsigned idTypeSize = typeSizeInWords(typeId);
437
LoopDawg8004d362017-09-17 10:38:52 -0600438 if (errorLatch)
439 return false;
440
steve-lunarg811d9f42016-08-17 08:33:49 -0600441 if (idTypeSize != 0)
442 idTypeSizeMap[resultId] = idTypeSize;
443 }
444 }
445
John Kessenich140f3df2015-06-26 16:58:36 -0600446 if (opCode == spv::Op::OpName) {
447 const spv::Id target = asId(start+1);
448 const std::string name = literalString(start+2);
449 nameMap[name] = target;
450
451 } else if (opCode == spv::Op::OpFunctionCall) {
452 ++fnCalls[asId(start + 3)];
453 } else if (opCode == spv::Op::OpEntryPoint) {
454 entryPoint = asId(start + 2);
455 } else if (opCode == spv::Op::OpFunction) {
LoopDawg8004d362017-09-17 10:38:52 -0600456 if (fnStart != 0) {
John Kessenich140f3df2015-06-26 16:58:36 -0600457 error("nested function found");
LoopDawg8004d362017-09-17 10:38:52 -0600458 return false;
459 }
460
John Kessenich140f3df2015-06-26 16:58:36 -0600461 fnStart = start;
462 fnRes = asId(start + 2);
463 } else if (opCode == spv::Op::OpFunctionEnd) {
464 assert(fnRes != spv::NoResult);
LoopDawg8004d362017-09-17 10:38:52 -0600465 if (fnStart == 0) {
John Kessenich140f3df2015-06-26 16:58:36 -0600466 error("function end without function start");
LoopDawg8004d362017-09-17 10:38:52 -0600467 return false;
468 }
469
John Kessenich140f3df2015-06-26 16:58:36 -0600470 fnPos[fnRes] = range_t(fnStart, start + asWordCount(start));
471 fnStart = 0;
472 } else if (isConstOp(opCode)) {
LoopDawg8004d362017-09-17 10:38:52 -0600473 if (errorLatch)
474 return false;
475
John Kessenich140f3df2015-06-26 16:58:36 -0600476 assert(asId(start + 2) != spv::NoResult);
477 typeConstPos.insert(start);
John Kessenich140f3df2015-06-26 16:58:36 -0600478 } else if (isTypeOp(opCode)) {
479 assert(asId(start + 1) != spv::NoResult);
480 typeConstPos.insert(start);
John Kessenich140f3df2015-06-26 16:58:36 -0600481 }
482
483 return false;
484 },
485
486 [this](spv::Id& id) { localId(id, unmapped); }
487 );
488 }
489
490 // Validate the SPIR header
491 void spirvbin_t::validate() const
492 {
493 msg(2, 2, std::string("validating: "));
494
LoopDawg8004d362017-09-17 10:38:52 -0600495 if (spv.size() < header_size) {
John Kessenich140f3df2015-06-26 16:58:36 -0600496 error("file too short: ");
LoopDawg8004d362017-09-17 10:38:52 -0600497 return;
498 }
John Kessenich140f3df2015-06-26 16:58:36 -0600499
LoopDawg8004d362017-09-17 10:38:52 -0600500 if (magic() != spv::MagicNumber) {
John Kessenich140f3df2015-06-26 16:58:36 -0600501 error("bad magic number");
LoopDawg8004d362017-09-17 10:38:52 -0600502 return;
503 }
John Kessenich140f3df2015-06-26 16:58:36 -0600504
505 // field 1 = version
506 // field 2 = generator magic
507 // field 3 = result <id> bound
508
LoopDawg8004d362017-09-17 10:38:52 -0600509 if (schemaNum() != 0) {
John Kessenich140f3df2015-06-26 16:58:36 -0600510 error("bad schema, must be 0");
LoopDawg8004d362017-09-17 10:38:52 -0600511 return;
512 }
John Kessenich140f3df2015-06-26 16:58:36 -0600513 }
514
John Kessenich140f3df2015-06-26 16:58:36 -0600515 int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn)
516 {
517 const auto instructionStart = word;
518 const unsigned wordCount = asWordCount(instructionStart);
John Kessenich140f3df2015-06-26 16:58:36 -0600519 const int nextInst = word++ + wordCount;
LoopDawg65c2eed2017-05-18 16:13:04 -0600520 spv::Op opCode = asOpCode(instructionStart);
John Kessenich140f3df2015-06-26 16:58:36 -0600521
LoopDawg8004d362017-09-17 10:38:52 -0600522 if (nextInst > int(spv.size())) {
John Kessenich140f3df2015-06-26 16:58:36 -0600523 error("spir instruction terminated too early");
LoopDawg8004d362017-09-17 10:38:52 -0600524 return -1;
525 }
John Kessenich140f3df2015-06-26 16:58:36 -0600526
527 // Base for computing number of operands; will be updated as more is learned
528 unsigned numOperands = wordCount - 1;
529
530 if (instFn(opCode, instructionStart))
531 return nextInst;
532
533 // Read type and result ID from instruction desc table
534 if (spv::InstructionDesc[opCode].hasType()) {
535 idFn(asId(word++));
536 --numOperands;
537 }
538
539 if (spv::InstructionDesc[opCode].hasResult()) {
540 idFn(asId(word++));
541 --numOperands;
542 }
543
544 // Extended instructions: currently, assume everything is an ID.
545 // TODO: add whatever data we need for exceptions to that
546 if (opCode == spv::OpExtInst) {
547 word += 2; // instruction set, and instruction from set
548 numOperands -= 2;
549
550 for (unsigned op=0; op < numOperands; ++op)
551 idFn(asId(word++)); // ID
552
553 return nextInst;
554 }
555
steve-lunarg811d9f42016-08-17 08:33:49 -0600556 // Circular buffer so we can look back at previous unmapped values during the mapping pass.
557 static const unsigned idBufferSize = 4;
558 spv::Id idBuffer[idBufferSize];
559 unsigned idBufferPos = 0;
560
John Kessenich140f3df2015-06-26 16:58:36 -0600561 // Store IDs from instruction in our map
John Kessenich6c292d32016-02-15 20:58:50 -0700562 for (int op = 0; numOperands > 0; ++op, --numOperands) {
LoopDawg65c2eed2017-05-18 16:13:04 -0600563 // SpecConstantOp is special: it includes the operands of another opcode which is
564 // given as a literal in the 3rd word. We will switch over to pretending that the
565 // opcode being processed is the literal opcode value of the SpecConstantOp. See the
566 // SPIRV spec for details. This way we will handle IDs and literals as appropriate for
567 // the embedded op.
568 if (opCode == spv::OpSpecConstantOp) {
569 if (op == 0) {
570 opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp.
571 --numOperands;
572 }
573 }
574
John Kessenich140f3df2015-06-26 16:58:36 -0600575 switch (spv::InstructionDesc[opCode].operands.getClass(op)) {
576 case spv::OperandId:
Pyry Haulosb93e3a32016-07-22 09:57:28 -0700577 case spv::OperandScope:
578 case spv::OperandMemorySemantics:
steve-lunarg811d9f42016-08-17 08:33:49 -0600579 idBuffer[idBufferPos] = asId(word);
580 idBufferPos = (idBufferPos + 1) % idBufferSize;
John Kessenich140f3df2015-06-26 16:58:36 -0600581 idFn(asId(word++));
582 break;
583
John Kessenich140f3df2015-06-26 16:58:36 -0600584 case spv::OperandVariableIds:
585 for (unsigned i = 0; i < numOperands; ++i)
586 idFn(asId(word++));
587 return nextInst;
588
589 case spv::OperandVariableLiterals:
Steve465a1462015-07-15 14:34:35 -0600590 // for clarity
591 // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) {
592 // ++word;
593 // --numOperands;
594 // }
595 // word += numOperands;
John Kessenich140f3df2015-06-26 16:58:36 -0600596 return nextInst;
597
steve-lunarg811d9f42016-08-17 08:33:49 -0600598 case spv::OperandVariableLiteralId: {
599 if (opCode == OpSwitch) {
600 // word-2 is the position of the selector ID. OpSwitch Literals match its type.
601 // In case the IDs are currently being remapped, we get the word[-2] ID from
602 // the circular idBuffer.
603 const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize;
604 const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]);
605 const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize);
606
LoopDawg8004d362017-09-17 10:38:52 -0600607 if (errorLatch)
608 return -1;
609
steve-lunarg811d9f42016-08-17 08:33:49 -0600610 for (unsigned arg=0; arg<numLiteralIdPairs; ++arg) {
611 word += literalSize; // literal
612 idFn(asId(word++)); // label
613 }
614 } else {
615 assert(0); // currentely, only OpSwitch uses OperandVariableLiteralId
John Kessenich140f3df2015-06-26 16:58:36 -0600616 }
steve-lunarg811d9f42016-08-17 08:33:49 -0600617
John Kessenich140f3df2015-06-26 16:58:36 -0600618 return nextInst;
steve-lunarg811d9f42016-08-17 08:33:49 -0600619 }
John Kessenich140f3df2015-06-26 16:58:36 -0600620
GregF8548bab2016-02-01 16:44:57 -0700621 case spv::OperandLiteralString: {
622 const int stringWordCount = literalStringWords(literalString(word));
623 word += stringWordCount;
624 numOperands -= (stringWordCount-1); // -1 because for() header post-decrements
625 break;
626 }
627
628 // Execution mode might have extra literal operands. Skip them.
629 case spv::OperandExecutionMode:
John Kessenich140f3df2015-06-26 16:58:36 -0600630 return nextInst;
631
GregF8548bab2016-02-01 16:44:57 -0700632 // Single word operands we simply ignore, as they hold no IDs
John Kessenich140f3df2015-06-26 16:58:36 -0600633 case spv::OperandLiteralNumber:
634 case spv::OperandSource:
635 case spv::OperandExecutionModel:
636 case spv::OperandAddressing:
637 case spv::OperandMemory:
John Kessenich140f3df2015-06-26 16:58:36 -0600638 case spv::OperandStorage:
639 case spv::OperandDimensionality:
GregF8548bab2016-02-01 16:44:57 -0700640 case spv::OperandSamplerAddressingMode:
641 case spv::OperandSamplerFilterMode:
642 case spv::OperandSamplerImageFormat:
643 case spv::OperandImageChannelOrder:
644 case spv::OperandImageChannelDataType:
GregF3bb040b2016-01-05 08:41:59 -0700645 case spv::OperandImageOperands:
GregF8548bab2016-02-01 16:44:57 -0700646 case spv::OperandFPFastMath:
647 case spv::OperandFPRoundingMode:
648 case spv::OperandLinkageType:
649 case spv::OperandAccessQualifier:
650 case spv::OperandFuncParamAttr:
John Kessenich140f3df2015-06-26 16:58:36 -0600651 case spv::OperandDecoration:
652 case spv::OperandBuiltIn:
653 case spv::OperandSelect:
654 case spv::OperandLoop:
655 case spv::OperandFunction:
John Kessenich140f3df2015-06-26 16:58:36 -0600656 case spv::OperandMemoryAccess:
John Kessenich140f3df2015-06-26 16:58:36 -0600657 case spv::OperandGroupOperation:
658 case spv::OperandKernelEnqueueFlags:
659 case spv::OperandKernelProfilingInfo:
GregF8548bab2016-02-01 16:44:57 -0700660 case spv::OperandCapability:
John Kessenich140f3df2015-06-26 16:58:36 -0600661 ++word;
662 break;
663
664 default:
GregF036a7942016-01-05 16:41:29 -0700665 assert(0 && "Unhandled Operand Class");
John Kessenich140f3df2015-06-26 16:58:36 -0600666 break;
667 }
668 }
669
670 return nextInst;
671 }
672
673 // Make a pass over all the instructions and process them given appropriate functions
674 spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, unsigned begin, unsigned end)
675 {
676 // For efficiency, reserve name map space. It can grow if needed.
677 nameMap.reserve(32);
678
679 // If begin or end == 0, use defaults
680 begin = (begin == 0 ? header_size : begin);
681 end = (end == 0 ? unsigned(spv.size()) : end);
682
683 // basic parsing and InstructionDesc table borrowed from SpvDisassemble.cpp...
684 unsigned nextInst = unsigned(spv.size());
685
LoopDawg8004d362017-09-17 10:38:52 -0600686 for (unsigned word = begin; word < end; word = nextInst) {
John Kessenich140f3df2015-06-26 16:58:36 -0600687 nextInst = processInstruction(word, instFn, idFn);
688
LoopDawg8004d362017-09-17 10:38:52 -0600689 if (errorLatch)
690 return *this;
691 }
692
John Kessenich140f3df2015-06-26 16:58:36 -0600693 return *this;
694 }
695
696 // Apply global name mapping to a single module
697 void spirvbin_t::mapNames()
698 {
699 static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options
700 static const std::uint32_t firstMappedID = 3019; // offset into ID space
701
702 for (const auto& name : nameMap) {
703 std::uint32_t hashval = 1911;
704 for (const char c : name.first)
705 hashval = hashval * 1009 + c;
706
LoopDawg8004d362017-09-17 10:38:52 -0600707 if (isOldIdUnmapped(name.second)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600708 localId(name.second, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
LoopDawg8004d362017-09-17 10:38:52 -0600709 if (errorLatch)
710 return;
711 }
John Kessenich140f3df2015-06-26 16:58:36 -0600712 }
713 }
714
715 // Map fn contents to IDs of similar functions in other modules
716 void spirvbin_t::mapFnBodies()
717 {
718 static const std::uint32_t softTypeIdLimit = 19071; // small prime. TODO: get from options
719 static const std::uint32_t firstMappedID = 6203; // offset into ID space
720
721 // Initial approach: go through some high priority opcodes first and assign them
722 // hash values.
723
724 spv::Id fnId = spv::NoResult;
725 std::vector<unsigned> instPos;
726 instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed.
727
728 // Build local table of instruction start positions
729 process(
730 [&](spv::Op, unsigned start) { instPos.push_back(start); return true; },
731 op_fn_nop);
732
LoopDawg8004d362017-09-17 10:38:52 -0600733 if (errorLatch)
734 return;
735
John Kessenich140f3df2015-06-26 16:58:36 -0600736 // Window size for context-sensitive canonicalization values
Eric Engestrom6a6d6dd2016-04-03 01:17:13 +0100737 // Empirical best size from a single data set. TODO: Would be a good tunable.
GregF8548bab2016-02-01 16:44:57 -0700738 // We essentially perform a little convolution around each instruction,
John Kessenich140f3df2015-06-26 16:58:36 -0600739 // to capture the flavor of nearby code, to hopefully match to similar
740 // code in other modules.
741 static const unsigned windowSize = 2;
742
743 for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) {
744 const unsigned start = instPos[entry];
745 const spv::Op opCode = asOpCode(start);
746
747 if (opCode == spv::OpFunction)
748 fnId = asId(start + 2);
749
750 if (opCode == spv::OpFunctionEnd)
751 fnId = spv::NoResult;
752
753 if (fnId != spv::NoResult) { // if inside a function
754 if (spv::InstructionDesc[opCode].hasResult()) {
755 const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);
756 const spv::Id resId = asId(word);
757 std::uint32_t hashval = fnId * 17; // small prime
758
759 for (unsigned i = entry-1; i >= entry-windowSize; --i) {
760 if (asOpCode(instPos[i]) == spv::OpFunction)
761 break;
762 hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
763 }
764
765 for (unsigned i = entry; i <= entry + windowSize; ++i) {
766 if (asOpCode(instPos[i]) == spv::OpFunctionEnd)
767 break;
768 hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
769 }
770
LoopDawg8004d362017-09-17 10:38:52 -0600771 if (isOldIdUnmapped(resId)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600772 localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
LoopDawg8004d362017-09-17 10:38:52 -0600773 if (errorLatch)
774 return;
775 }
776
John Kessenich140f3df2015-06-26 16:58:36 -0600777 }
778 }
779 }
780
781 spv::Op thisOpCode(spv::OpNop);
782 std::unordered_map<int, int> opCounter;
783 int idCounter(0);
784 fnId = spv::NoResult;
785
786 process(
787 [&](spv::Op opCode, unsigned start) {
788 switch (opCode) {
789 case spv::OpFunction:
790 // Reset counters at each function
791 idCounter = 0;
792 opCounter.clear();
793 fnId = asId(start + 2);
794 break;
795
John Kessenich5e4b1242015-08-06 22:53:06 -0600796 case spv::OpImageSampleImplicitLod:
797 case spv::OpImageSampleExplicitLod:
798 case spv::OpImageSampleDrefImplicitLod:
799 case spv::OpImageSampleDrefExplicitLod:
800 case spv::OpImageSampleProjImplicitLod:
801 case spv::OpImageSampleProjExplicitLod:
802 case spv::OpImageSampleProjDrefImplicitLod:
803 case spv::OpImageSampleProjDrefExplicitLod:
John Kessenich140f3df2015-06-26 16:58:36 -0600804 case spv::OpDot:
805 case spv::OpCompositeExtract:
806 case spv::OpCompositeInsert:
807 case spv::OpVectorShuffle:
808 case spv::OpLabel:
809 case spv::OpVariable:
810
811 case spv::OpAccessChain:
812 case spv::OpLoad:
813 case spv::OpStore:
814 case spv::OpCompositeConstruct:
815 case spv::OpFunctionCall:
816 ++opCounter[opCode];
817 idCounter = 0;
818 thisOpCode = opCode;
819 break;
820 default:
821 thisOpCode = spv::OpNop;
822 }
823
824 return false;
825 },
826
827 [&](spv::Id& id) {
828 if (thisOpCode != spv::OpNop) {
829 ++idCounter;
830 const std::uint32_t hashval = opCounter[thisOpCode] * thisOpCode * 50047 + idCounter + fnId * 117;
831
832 if (isOldIdUnmapped(id))
833 localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
834 }
835 });
836 }
837
838 // EXPERIMENTAL: forward IO and uniform load/stores into operands
839 // This produces invalid Schema-0 SPIRV
840 void spirvbin_t::forwardLoadStores()
841 {
842 idset_t fnLocalVars; // set of function local vars
843 idmap_t idMap; // Map of load result IDs to what they load
844
845 // EXPERIMENTAL: Forward input and access chain loads into consumptions
846 process(
847 [&](spv::Op opCode, unsigned start) {
848 // Add inputs and uniforms to the map
John Kessenich5e4b1242015-08-06 22:53:06 -0600849 if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
John Kessenich140f3df2015-06-26 16:58:36 -0600850 (spv[start+3] == spv::StorageClassUniform ||
851 spv[start+3] == spv::StorageClassUniformConstant ||
852 spv[start+3] == spv::StorageClassInput))
853 fnLocalVars.insert(asId(start+2));
854
855 if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0)
856 fnLocalVars.insert(asId(start+2));
857
858 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
859 idMap[asId(start+2)] = asId(start+3);
860 stripInst(start);
861 }
862
863 return false;
864 },
865
866 [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
867 );
868
LoopDawg8004d362017-09-17 10:38:52 -0600869 if (errorLatch)
870 return;
871
John Kessenich140f3df2015-06-26 16:58:36 -0600872 // EXPERIMENTAL: Implicit output stores
873 fnLocalVars.clear();
874 idMap.clear();
875
876 process(
877 [&](spv::Op opCode, unsigned start) {
878 // Add inputs and uniforms to the map
John Kessenich5e4b1242015-08-06 22:53:06 -0600879 if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
John Kessenich140f3df2015-06-26 16:58:36 -0600880 (spv[start+3] == spv::StorageClassOutput))
881 fnLocalVars.insert(asId(start+2));
882
883 if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
884 idMap[asId(start+2)] = asId(start+1);
885 stripInst(start);
886 }
887
888 return false;
889 },
890 op_fn_nop);
891
LoopDawg8004d362017-09-17 10:38:52 -0600892 if (errorLatch)
893 return;
894
John Kessenich140f3df2015-06-26 16:58:36 -0600895 process(
896 inst_fn_nop,
897 [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
898 );
899
LoopDawg8004d362017-09-17 10:38:52 -0600900 if (errorLatch)
901 return;
902
John Kessenich140f3df2015-06-26 16:58:36 -0600903 strip(); // strip out data we decided to eliminate
904 }
905
GregF8548bab2016-02-01 16:44:57 -0700906 // optimize loads and stores
John Kessenich140f3df2015-06-26 16:58:36 -0600907 void spirvbin_t::optLoadStore()
908 {
GregF8548bab2016-02-01 16:44:57 -0700909 idset_t fnLocalVars; // candidates for removal (only locals)
910 idmap_t idMap; // Map of load result IDs to what they load
911 blockmap_t blockMap; // Map of IDs to blocks they first appear in
912 int blockNum = 0; // block count, to avoid crossing flow control
John Kessenich140f3df2015-06-26 16:58:36 -0600913
914 // Find all the function local pointers stored at most once, and not via access chains
915 process(
916 [&](spv::Op opCode, unsigned start) {
917 const int wordCount = asWordCount(start);
918
GregF8548bab2016-02-01 16:44:57 -0700919 // Count blocks, so we can avoid crossing flow control
920 if (isFlowCtrl(opCode))
921 ++blockNum;
922
John Kessenich140f3df2015-06-26 16:58:36 -0600923 // Add local variables to the map
GregF796e15a2016-01-05 13:00:04 -0700924 if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) {
John Kessenich140f3df2015-06-26 16:58:36 -0600925 fnLocalVars.insert(asId(start+2));
GregF796e15a2016-01-05 13:00:04 -0700926 return true;
927 }
John Kessenich140f3df2015-06-26 16:58:36 -0600928
929 // Ignore process vars referenced via access chain
930 if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) {
931 fnLocalVars.erase(asId(start+3));
932 idMap.erase(asId(start+3));
GregF796e15a2016-01-05 13:00:04 -0700933 return true;
John Kessenich140f3df2015-06-26 16:58:36 -0600934 }
935
936 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
GregF8548bab2016-02-01 16:44:57 -0700937 const spv::Id varId = asId(start+3);
938
939 // Avoid loads before stores
940 if (idMap.find(varId) == idMap.end()) {
941 fnLocalVars.erase(varId);
942 idMap.erase(varId);
John Kessenich140f3df2015-06-26 16:58:36 -0600943 }
944
945 // don't do for volatile references
946 if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) {
GregF8548bab2016-02-01 16:44:57 -0700947 fnLocalVars.erase(varId);
948 idMap.erase(varId);
John Kessenich140f3df2015-06-26 16:58:36 -0600949 }
GregF8548bab2016-02-01 16:44:57 -0700950
951 // Handle flow control
952 if (blockMap.find(varId) == blockMap.end()) {
953 blockMap[varId] = blockNum; // track block we found it in.
954 } else if (blockMap[varId] != blockNum) {
955 fnLocalVars.erase(varId); // Ignore if crosses flow control
956 idMap.erase(varId);
957 }
958
GregF796e15a2016-01-05 13:00:04 -0700959 return true;
John Kessenich140f3df2015-06-26 16:58:36 -0600960 }
961
962 if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
GregF8548bab2016-02-01 16:44:57 -0700963 const spv::Id varId = asId(start+1);
964
965 if (idMap.find(varId) == idMap.end()) {
966 idMap[varId] = asId(start+2);
John Kessenich140f3df2015-06-26 16:58:36 -0600967 } else {
968 // Remove if it has more than one store to the same pointer
GregF8548bab2016-02-01 16:44:57 -0700969 fnLocalVars.erase(varId);
970 idMap.erase(varId);
John Kessenich140f3df2015-06-26 16:58:36 -0600971 }
972
973 // don't do for volatile references
974 if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) {
975 fnLocalVars.erase(asId(start+3));
976 idMap.erase(asId(start+3));
977 }
GregF8548bab2016-02-01 16:44:57 -0700978
979 // Handle flow control
980 if (blockMap.find(varId) == blockMap.end()) {
981 blockMap[varId] = blockNum; // track block we found it in.
982 } else if (blockMap[varId] != blockNum) {
983 fnLocalVars.erase(varId); // Ignore if crosses flow control
984 idMap.erase(varId);
985 }
986
GregF796e15a2016-01-05 13:00:04 -0700987 return true;
John Kessenich140f3df2015-06-26 16:58:36 -0600988 }
989
GregF796e15a2016-01-05 13:00:04 -0700990 return false;
John Kessenich140f3df2015-06-26 16:58:36 -0600991 },
GregF796e15a2016-01-05 13:00:04 -0700992
993 // If local var id used anywhere else, don't eliminate
John Kessenichecba76f2017-01-06 00:34:48 -0700994 [&](spv::Id& id) {
GregF796e15a2016-01-05 13:00:04 -0700995 if (fnLocalVars.count(id) > 0) {
996 fnLocalVars.erase(id);
997 idMap.erase(id);
998 }
999 }
1000 );
John Kessenich140f3df2015-06-26 16:58:36 -06001001
LoopDawg8004d362017-09-17 10:38:52 -06001002 if (errorLatch)
1003 return;
1004
John Kessenich140f3df2015-06-26 16:58:36 -06001005 process(
1006 [&](spv::Op opCode, unsigned start) {
1007 if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0)
1008 idMap[asId(start+2)] = idMap[asId(start+3)];
1009 return false;
1010 },
1011 op_fn_nop);
1012
LoopDawg8004d362017-09-17 10:38:52 -06001013 if (errorLatch)
1014 return;
1015
GregF8548bab2016-02-01 16:44:57 -07001016 // Chase replacements to their origins, in case there is a chain such as:
1017 // 2 = store 1
1018 // 3 = load 2
1019 // 4 = store 3
1020 // 5 = load 4
1021 // We want to replace uses of 5 with 1.
1022 for (const auto& idPair : idMap) {
1023 spv::Id id = idPair.first;
1024 while (idMap.find(id) != idMap.end()) // Chase to end of chain
1025 id = idMap[id];
1026
1027 idMap[idPair.first] = id; // replace with final result
1028 }
1029
John Kessenich140f3df2015-06-26 16:58:36 -06001030 // Remove the load/store/variables for the ones we've discovered
1031 process(
1032 [&](spv::Op opCode, unsigned start) {
1033 if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) ||
1034 (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) ||
1035 (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) {
GregF8548bab2016-02-01 16:44:57 -07001036
John Kessenich140f3df2015-06-26 16:58:36 -06001037 stripInst(start);
1038 return true;
1039 }
1040
1041 return false;
1042 },
1043
GregF8548bab2016-02-01 16:44:57 -07001044 [&](spv::Id& id) {
1045 if (idMap.find(id) != idMap.end()) id = idMap[id];
1046 }
John Kessenich140f3df2015-06-26 16:58:36 -06001047 );
1048
LoopDawg8004d362017-09-17 10:38:52 -06001049 if (errorLatch)
1050 return;
1051
John Kessenich140f3df2015-06-26 16:58:36 -06001052 strip(); // strip out data we decided to eliminate
1053 }
1054
1055 // remove bodies of uncalled functions
1056 void spirvbin_t::dceFuncs()
1057 {
1058 msg(3, 2, std::string("Removing Dead Functions: "));
1059
1060 // TODO: There are more efficient ways to do this.
1061 bool changed = true;
1062
1063 while (changed) {
1064 changed = false;
1065
1066 for (auto fn = fnPos.begin(); fn != fnPos.end(); ) {
1067 if (fn->first == entryPoint) { // don't DCE away the entry point!
1068 ++fn;
1069 continue;
1070 }
1071
1072 const auto call_it = fnCalls.find(fn->first);
1073
1074 if (call_it == fnCalls.end() || call_it->second == 0) {
1075 changed = true;
1076 stripRange.push_back(fn->second);
John Kessenich140f3df2015-06-26 16:58:36 -06001077
1078 // decrease counts of called functions
1079 process(
1080 [&](spv::Op opCode, unsigned start) {
1081 if (opCode == spv::Op::OpFunctionCall) {
1082 const auto call_it = fnCalls.find(asId(start + 3));
1083 if (call_it != fnCalls.end()) {
1084 if (--call_it->second <= 0)
1085 fnCalls.erase(call_it);
1086 }
1087 }
1088
1089 return true;
1090 },
1091 op_fn_nop,
1092 fn->second.first,
1093 fn->second.second);
1094
LoopDawg8004d362017-09-17 10:38:52 -06001095 if (errorLatch)
1096 return;
1097
John Kessenich140f3df2015-06-26 16:58:36 -06001098 fn = fnPos.erase(fn);
1099 } else ++fn;
1100 }
1101 }
1102 }
1103
1104 // remove unused function variables + decorations
1105 void spirvbin_t::dceVars()
1106 {
1107 msg(3, 2, std::string("DCE Vars: "));
1108
1109 std::unordered_map<spv::Id, int> varUseCount;
1110
1111 // Count function variable use
1112 process(
1113 [&](spv::Op opCode, unsigned start) {
GregF39d5e712016-06-03 09:53:47 -06001114 if (opCode == spv::OpVariable) {
1115 ++varUseCount[asId(start+2)];
1116 return true;
1117 } else if (opCode == spv::OpEntryPoint) {
1118 const int wordCount = asWordCount(start);
1119 for (int i = 4; i < wordCount; i++) {
1120 ++varUseCount[asId(start+i)];
1121 }
1122 return true;
1123 } else
1124 return false;
John Kessenich140f3df2015-06-26 16:58:36 -06001125 },
1126
1127 [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; }
1128 );
1129
LoopDawg8004d362017-09-17 10:38:52 -06001130 if (errorLatch)
1131 return;
1132
John Kessenich140f3df2015-06-26 16:58:36 -06001133 // Remove single-use function variables + associated decorations and names
1134 process(
1135 [&](spv::Op opCode, unsigned start) {
steve-lunarg297754c2016-12-09 11:13:23 -07001136 spv::Id id = spv::NoResult;
1137 if (opCode == spv::OpVariable)
1138 id = asId(start+2);
1139 if (opCode == spv::OpDecorate || opCode == spv::OpName)
1140 id = asId(start+1);
1141
1142 if (id != spv::NoResult && varUseCount[id] == 1)
1143 stripInst(start);
1144
John Kessenich140f3df2015-06-26 16:58:36 -06001145 return true;
1146 },
1147 op_fn_nop);
1148 }
1149
1150 // remove unused types
1151 void spirvbin_t::dceTypes()
1152 {
1153 std::vector<bool> isType(bound(), false);
1154
1155 // for speed, make O(1) way to get to type query (map is log(n))
1156 for (const auto typeStart : typeConstPos)
1157 isType[asTypeConstId(typeStart)] = true;
1158
1159 std::unordered_map<spv::Id, int> typeUseCount;
1160
steve-lunarg811d9f42016-08-17 08:33:49 -06001161 // This is not the most efficient algorithm, but this is an offline tool, and
1162 // it's easy to write this way. Can be improved opportunistically if needed.
1163 bool changed = true;
1164 while (changed) {
1165 changed = false;
1166 strip();
1167 typeUseCount.clear();
John Kessenich140f3df2015-06-26 16:58:36 -06001168
steve-lunarg811d9f42016-08-17 08:33:49 -06001169 // Count total type usage
John Kessenich140f3df2015-06-26 16:58:36 -06001170 process(inst_fn_nop,
steve-lunarg811d9f42016-08-17 08:33:49 -06001171 [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; }
1172 );
John Kessenich140f3df2015-06-26 16:58:36 -06001173
LoopDawg8004d362017-09-17 10:38:52 -06001174 if (errorLatch)
1175 return;
1176
steve-lunarg811d9f42016-08-17 08:33:49 -06001177 // Remove single reference types
1178 for (const auto typeStart : typeConstPos) {
1179 const spv::Id typeId = asTypeConstId(typeStart);
1180 if (typeUseCount[typeId] == 1) {
1181 changed = true;
1182 --typeUseCount[typeId];
1183 stripInst(typeStart);
1184 }
John Kessenich140f3df2015-06-26 16:58:36 -06001185 }
LoopDawg8004d362017-09-17 10:38:52 -06001186
1187 if (errorLatch)
1188 return;
John Kessenich140f3df2015-06-26 16:58:36 -06001189 }
1190 }
1191
John Kessenich140f3df2015-06-26 16:58:36 -06001192#ifdef NOTDEF
1193 bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const
1194 {
1195 // Find the local type id "lt" and global type id "gt"
1196 const auto lt_it = typeConstPosR.find(lt);
1197 if (lt_it == typeConstPosR.end())
1198 return false;
1199
1200 const auto typeStart = lt_it->second;
1201
1202 // Search for entry in global table
1203 const auto gtype = globalTypes.find(gt);
1204 if (gtype == globalTypes.end())
1205 return false;
1206
1207 const auto& gdata = gtype->second;
1208
1209 // local wordcount and opcode
1210 const int wordCount = asWordCount(typeStart);
1211 const spv::Op opCode = asOpCode(typeStart);
1212
1213 // no type match if opcodes don't match, or operand count doesn't match
1214 if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0]))
1215 return false;
1216
1217 const unsigned numOperands = wordCount - 2; // all types have a result
1218
1219 const auto cmpIdRange = [&](range_t range) {
1220 for (int x=range.first; x<std::min(range.second, wordCount); ++x)
1221 if (!matchType(globalTypes, asId(typeStart+x), gdata[x]))
1222 return false;
1223 return true;
1224 };
1225
1226 const auto cmpConst = [&]() { return cmpIdRange(constRange(opCode)); };
1227 const auto cmpSubType = [&]() { return cmpIdRange(typeRange(opCode)); };
1228
1229 // Compare literals in range [start,end)
1230 const auto cmpLiteral = [&]() {
1231 const auto range = literalRange(opCode);
1232 return std::equal(spir.begin() + typeStart + range.first,
1233 spir.begin() + typeStart + std::min(range.second, wordCount),
1234 gdata.begin() + range.first);
1235 };
1236
1237 assert(isTypeOp(opCode) || isConstOp(opCode));
1238
1239 switch (opCode) {
1240 case spv::OpTypeOpaque: // TODO: disable until we compare the literal strings.
1241 case spv::OpTypeQueue: return false;
1242 case spv::OpTypeEvent: // fall through...
1243 case spv::OpTypeDeviceEvent: // ...
1244 case spv::OpTypeReserveId: return false;
1245 // for samplers, we don't handle the optional parameters yet
1246 case spv::OpTypeSampler: return cmpLiteral() && cmpConst() && cmpSubType() && wordCount == 8;
1247 default: return cmpLiteral() && cmpConst() && cmpSubType();
1248 }
1249 }
1250
John Kessenich140f3df2015-06-26 16:58:36 -06001251 // Look for an equivalent type in the globalTypes map
1252 spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt) const
1253 {
1254 // Try a recursive type match on each in turn, and return a match if we find one
1255 for (const auto& gt : globalTypes)
1256 if (matchType(globalTypes, lt, gt.first))
1257 return gt.first;
1258
1259 return spv::NoType;
1260 }
1261#endif // NOTDEF
1262
steve-lunarg811d9f42016-08-17 08:33:49 -06001263 // Return start position in SPV of given Id. error if not found.
1264 unsigned spirvbin_t::idPos(spv::Id id) const
John Kessenich140f3df2015-06-26 16:58:36 -06001265 {
steve-lunarg811d9f42016-08-17 08:33:49 -06001266 const auto tid_it = idPosR.find(id);
LoopDawg8004d362017-09-17 10:38:52 -06001267 if (tid_it == idPosR.end()) {
steve-lunarg811d9f42016-08-17 08:33:49 -06001268 error("ID not found");
LoopDawg8004d362017-09-17 10:38:52 -06001269 return 0;
1270 }
John Kessenich140f3df2015-06-26 16:58:36 -06001271
1272 return tid_it->second;
1273 }
1274
1275 // Hash types to canonical values. This can return ID collisions (it's a bit
1276 // inevitable): it's up to the caller to handle that gracefully.
1277 std::uint32_t spirvbin_t::hashType(unsigned typeStart) const
1278 {
1279 const unsigned wordCount = asWordCount(typeStart);
1280 const spv::Op opCode = asOpCode(typeStart);
1281
1282 switch (opCode) {
1283 case spv::OpTypeVoid: return 0;
1284 case spv::OpTypeBool: return 1;
1285 case spv::OpTypeInt: return 3 + (spv[typeStart+3]);
1286 case spv::OpTypeFloat: return 5;
1287 case spv::OpTypeVector:
steve-lunarg811d9f42016-08-17 08:33:49 -06001288 return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
John Kessenich140f3df2015-06-26 16:58:36 -06001289 case spv::OpTypeMatrix:
steve-lunarg811d9f42016-08-17 08:33:49 -06001290 return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
John Kessenich5e4b1242015-08-06 22:53:06 -06001291 case spv::OpTypeImage:
steve-lunarg811d9f42016-08-17 08:33:49 -06001292 return 120 + hashType(idPos(spv[typeStart+2])) +
John Kessenich140f3df2015-06-26 16:58:36 -06001293 spv[typeStart+3] + // dimensionality
John Kessenich5e4b1242015-08-06 22:53:06 -06001294 spv[typeStart+4] * 8 * 16 + // depth
John Kessenich140f3df2015-06-26 16:58:36 -06001295 spv[typeStart+5] * 4 * 16 + // arrayed
John Kessenich5e4b1242015-08-06 22:53:06 -06001296 spv[typeStart+6] * 2 * 16 + // multisampled
1297 spv[typeStart+7] * 1 * 16; // format
1298 case spv::OpTypeSampler:
John Kessenich140f3df2015-06-26 16:58:36 -06001299 return 500;
Steve3573f2e2015-08-11 09:20:14 -06001300 case spv::OpTypeSampledImage:
1301 return 502;
John Kessenich140f3df2015-06-26 16:58:36 -06001302 case spv::OpTypeArray:
steve-lunarg811d9f42016-08-17 08:33:49 -06001303 return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3];
John Kessenich140f3df2015-06-26 16:58:36 -06001304 case spv::OpTypeRuntimeArray:
steve-lunarg811d9f42016-08-17 08:33:49 -06001305 return 5000 + hashType(idPos(spv[typeStart+2]));
John Kessenich140f3df2015-06-26 16:58:36 -06001306 case spv::OpTypeStruct:
1307 {
1308 std::uint32_t hash = 10000;
1309 for (unsigned w=2; w < wordCount; ++w)
steve-lunarg811d9f42016-08-17 08:33:49 -06001310 hash += w * hashType(idPos(spv[typeStart+w]));
John Kessenich140f3df2015-06-26 16:58:36 -06001311 return hash;
1312 }
1313
1314 case spv::OpTypeOpaque: return 6000 + spv[typeStart+2];
steve-lunarg811d9f42016-08-17 08:33:49 -06001315 case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3]));
John Kessenich140f3df2015-06-26 16:58:36 -06001316 case spv::OpTypeFunction:
1317 {
1318 std::uint32_t hash = 200000;
1319 for (unsigned w=2; w < wordCount; ++w)
steve-lunarg811d9f42016-08-17 08:33:49 -06001320 hash += w * hashType(idPos(spv[typeStart+w]));
John Kessenich140f3df2015-06-26 16:58:36 -06001321 return hash;
1322 }
1323
1324 case spv::OpTypeEvent: return 300000;
1325 case spv::OpTypeDeviceEvent: return 300001;
1326 case spv::OpTypeReserveId: return 300002;
1327 case spv::OpTypeQueue: return 300003;
1328 case spv::OpTypePipe: return 300004;
John Kessenich140f3df2015-06-26 16:58:36 -06001329 case spv::OpConstantTrue: return 300007;
1330 case spv::OpConstantFalse: return 300008;
John Kessenich140f3df2015-06-26 16:58:36 -06001331 case spv::OpConstantComposite:
1332 {
steve-lunarg811d9f42016-08-17 08:33:49 -06001333 std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1]));
John Kessenich140f3df2015-06-26 16:58:36 -06001334 for (unsigned w=3; w < wordCount; ++w)
steve-lunarg811d9f42016-08-17 08:33:49 -06001335 hash += w * hashType(idPos(spv[typeStart+w]));
John Kessenich140f3df2015-06-26 16:58:36 -06001336 return hash;
1337 }
1338 case spv::OpConstant:
1339 {
steve-lunarg811d9f42016-08-17 08:33:49 -06001340 std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1]));
John Kessenich140f3df2015-06-26 16:58:36 -06001341 for (unsigned w=3; w < wordCount; ++w)
1342 hash += w * spv[typeStart+w];
1343 return hash;
1344 }
GregFc6831d12018-06-19 13:47:50 -06001345 case spv::OpConstantNull:
1346 {
1347 std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1]));
1348 return hash;
1349 }
1350 case spv::OpConstantSampler:
1351 {
1352 std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1]));
1353 for (unsigned w=3; w < wordCount; ++w)
1354 hash += w * spv[typeStart+w];
1355 return hash;
1356 }
John Kessenich140f3df2015-06-26 16:58:36 -06001357
1358 default:
1359 error("unknown type opcode");
1360 return 0;
1361 }
1362 }
1363
1364 void spirvbin_t::mapTypeConst()
1365 {
1366 globaltypes_t globalTypeMap;
1367
1368 msg(3, 2, std::string("Remapping Consts & Types: "));
1369
1370 static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options
1371 static const std::uint32_t firstMappedID = 8; // offset into ID space
1372
1373 for (auto& typeStart : typeConstPos) {
1374 const spv::Id resId = asTypeConstId(typeStart);
1375 const std::uint32_t hashval = hashType(typeStart);
1376
LoopDawg8004d362017-09-17 10:38:52 -06001377 if (errorLatch)
1378 return;
1379
1380 if (isOldIdUnmapped(resId)) {
John Kessenich140f3df2015-06-26 16:58:36 -06001381 localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
LoopDawg8004d362017-09-17 10:38:52 -06001382 if (errorLatch)
1383 return;
1384 }
John Kessenich140f3df2015-06-26 16:58:36 -06001385 }
1386 }
1387
John Kessenich140f3df2015-06-26 16:58:36 -06001388 // Strip a single binary by removing ranges given in stripRange
1389 void spirvbin_t::strip()
1390 {
1391 if (stripRange.empty()) // nothing to do
1392 return;
1393
1394 // Sort strip ranges in order of traversal
1395 std::sort(stripRange.begin(), stripRange.end());
1396
1397 // Allocate a new binary big enough to hold old binary
1398 // We'll step this iterator through the strip ranges as we go through the binary
1399 auto strip_it = stripRange.begin();
1400
1401 int strippedPos = 0;
1402 for (unsigned word = 0; word < unsigned(spv.size()); ++word) {
GregFe86b4c82017-11-02 14:01:57 -06001403 while (strip_it != stripRange.end() && word >= strip_it->second)
John Kessenich140f3df2015-06-26 16:58:36 -06001404 ++strip_it;
1405
1406 if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second)
1407 spv[strippedPos++] = spv[word];
1408 }
1409
1410 spv.resize(strippedPos);
1411 stripRange.clear();
1412
1413 buildLocalMaps();
1414 }
1415
1416 // Strip a single binary by removing ranges given in stripRange
1417 void spirvbin_t::remap(std::uint32_t opts)
1418 {
1419 options = opts;
1420
1421 // Set up opcode tables from SpvDoc
1422 spv::Parameterize();
1423
steve-lunarg297754c2016-12-09 11:13:23 -07001424 validate(); // validate header
1425 buildLocalMaps(); // build ID maps
John Kessenich140f3df2015-06-26 16:58:36 -06001426
1427 msg(3, 4, std::string("ID bound: ") + std::to_string(bound()));
1428
steve-lunarg297754c2016-12-09 11:13:23 -07001429 if (options & STRIP) stripDebug();
LoopDawg8004d362017-09-17 10:38:52 -06001430 if (errorLatch) return;
1431
John Kessenich140f3df2015-06-26 16:58:36 -06001432 strip(); // strip out data we decided to eliminate
LoopDawg8004d362017-09-17 10:38:52 -06001433 if (errorLatch) return;
1434
John Kessenich140f3df2015-06-26 16:58:36 -06001435 if (options & OPT_LOADSTORE) optLoadStore();
LoopDawg8004d362017-09-17 10:38:52 -06001436 if (errorLatch) return;
1437
John Kessenich140f3df2015-06-26 16:58:36 -06001438 if (options & OPT_FWD_LS) forwardLoadStores();
LoopDawg8004d362017-09-17 10:38:52 -06001439 if (errorLatch) return;
1440
John Kessenich140f3df2015-06-26 16:58:36 -06001441 if (options & DCE_FUNCS) dceFuncs();
LoopDawg8004d362017-09-17 10:38:52 -06001442 if (errorLatch) return;
1443
John Kessenich140f3df2015-06-26 16:58:36 -06001444 if (options & DCE_VARS) dceVars();
LoopDawg8004d362017-09-17 10:38:52 -06001445 if (errorLatch) return;
1446
John Kessenich140f3df2015-06-26 16:58:36 -06001447 if (options & DCE_TYPES) dceTypes();
LoopDawg8004d362017-09-17 10:38:52 -06001448 if (errorLatch) return;
steve-lunarg297754c2016-12-09 11:13:23 -07001449
1450 strip(); // strip out data we decided to eliminate
LoopDawg8004d362017-09-17 10:38:52 -06001451 if (errorLatch) return;
1452
steve-lunarg297754c2016-12-09 11:13:23 -07001453 stripDeadRefs(); // remove references to things we DCEed
LoopDawg8004d362017-09-17 10:38:52 -06001454 if (errorLatch) return;
1455
steve-lunarg297754c2016-12-09 11:13:23 -07001456 // after the last strip, we must clean any debug info referring to now-deleted data
steve-lunarg811d9f42016-08-17 08:33:49 -06001457
John Kessenich140f3df2015-06-26 16:58:36 -06001458 if (options & MAP_TYPES) mapTypeConst();
LoopDawg8004d362017-09-17 10:38:52 -06001459 if (errorLatch) return;
1460
John Kessenich140f3df2015-06-26 16:58:36 -06001461 if (options & MAP_NAMES) mapNames();
LoopDawg8004d362017-09-17 10:38:52 -06001462 if (errorLatch) return;
1463
John Kessenich140f3df2015-06-26 16:58:36 -06001464 if (options & MAP_FUNCS) mapFnBodies();
LoopDawg8004d362017-09-17 10:38:52 -06001465 if (errorLatch) return;
John Kessenich140f3df2015-06-26 16:58:36 -06001466
steve-lunarg297754c2016-12-09 11:13:23 -07001467 if (options & MAP_ALL) {
1468 mapRemainder(); // map any unmapped IDs
LoopDawg8004d362017-09-17 10:38:52 -06001469 if (errorLatch) return;
1470
steve-lunarg297754c2016-12-09 11:13:23 -07001471 applyMap(); // Now remap each shader to the new IDs we've come up with
LoopDawg8004d362017-09-17 10:38:52 -06001472 if (errorLatch) return;
steve-lunarg297754c2016-12-09 11:13:23 -07001473 }
John Kessenich140f3df2015-06-26 16:58:36 -06001474 }
1475
1476 // remap from a memory image
1477 void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, std::uint32_t opts)
1478 {
1479 spv.swap(in_spv);
1480 remap(opts);
1481 spv.swap(in_spv);
1482 }
1483
1484} // namespace SPV
1485
1486#endif // defined (use_cpp11)
1487