1// smol-v - public domain - https://github.com/aras-p/smol-v
2// authored 2016-2020 by Aras Pranckevicius
3// no warranty implied; use at your own risk
4// See end of file for license information.
5
6#include "smolv.h"
7#include <stdint.h>
8#include <vector>
9#include <algorithm>
10#include <cstdio>
11#include <cstring>
12
13#if !defined(_MSC_VER) && __cplusplus < 201103L
14#define static_assert(x,y)
15#endif
16
17#define _SMOLV_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
18
19// --------------------------------------------------------------------------------------------
20// Metadata about known SPIR-V operations
21
22enum SpvOp
23{
24 SpvOpNop = 0,
25 SpvOpUndef = 1,
26 SpvOpSourceContinued = 2,
27 SpvOpSource = 3,
28 SpvOpSourceExtension = 4,
29 SpvOpName = 5,
30 SpvOpMemberName = 6,
31 SpvOpString = 7,
32 SpvOpLine = 8,
33 SpvOpExtension = 10,
34 SpvOpExtInstImport = 11,
35 SpvOpExtInst = 12,
36 SpvOpVectorShuffleCompact = 13, // not in SPIR-V, added for SMOL-V!
37 SpvOpMemoryModel = 14,
38 SpvOpEntryPoint = 15,
39 SpvOpExecutionMode = 16,
40 SpvOpCapability = 17,
41 SpvOpTypeVoid = 19,
42 SpvOpTypeBool = 20,
43 SpvOpTypeInt = 21,
44 SpvOpTypeFloat = 22,
45 SpvOpTypeVector = 23,
46 SpvOpTypeMatrix = 24,
47 SpvOpTypeImage = 25,
48 SpvOpTypeSampler = 26,
49 SpvOpTypeSampledImage = 27,
50 SpvOpTypeArray = 28,
51 SpvOpTypeRuntimeArray = 29,
52 SpvOpTypeStruct = 30,
53 SpvOpTypeOpaque = 31,
54 SpvOpTypePointer = 32,
55 SpvOpTypeFunction = 33,
56 SpvOpTypeEvent = 34,
57 SpvOpTypeDeviceEvent = 35,
58 SpvOpTypeReserveId = 36,
59 SpvOpTypeQueue = 37,
60 SpvOpTypePipe = 38,
61 SpvOpTypeForwardPointer = 39,
62 SpvOpConstantTrue = 41,
63 SpvOpConstantFalse = 42,
64 SpvOpConstant = 43,
65 SpvOpConstantComposite = 44,
66 SpvOpConstantSampler = 45,
67 SpvOpConstantNull = 46,
68 SpvOpSpecConstantTrue = 48,
69 SpvOpSpecConstantFalse = 49,
70 SpvOpSpecConstant = 50,
71 SpvOpSpecConstantComposite = 51,
72 SpvOpSpecConstantOp = 52,
73 SpvOpFunction = 54,
74 SpvOpFunctionParameter = 55,
75 SpvOpFunctionEnd = 56,
76 SpvOpFunctionCall = 57,
77 SpvOpVariable = 59,
78 SpvOpImageTexelPointer = 60,
79 SpvOpLoad = 61,
80 SpvOpStore = 62,
81 SpvOpCopyMemory = 63,
82 SpvOpCopyMemorySized = 64,
83 SpvOpAccessChain = 65,
84 SpvOpInBoundsAccessChain = 66,
85 SpvOpPtrAccessChain = 67,
86 SpvOpArrayLength = 68,
87 SpvOpGenericPtrMemSemantics = 69,
88 SpvOpInBoundsPtrAccessChain = 70,
89 SpvOpDecorate = 71,
90 SpvOpMemberDecorate = 72,
91 SpvOpDecorationGroup = 73,
92 SpvOpGroupDecorate = 74,
93 SpvOpGroupMemberDecorate = 75,
94 SpvOpVectorExtractDynamic = 77,
95 SpvOpVectorInsertDynamic = 78,
96 SpvOpVectorShuffle = 79,
97 SpvOpCompositeConstruct = 80,
98 SpvOpCompositeExtract = 81,
99 SpvOpCompositeInsert = 82,
100 SpvOpCopyObject = 83,
101 SpvOpTranspose = 84,
102 SpvOpSampledImage = 86,
103 SpvOpImageSampleImplicitLod = 87,
104 SpvOpImageSampleExplicitLod = 88,
105 SpvOpImageSampleDrefImplicitLod = 89,
106 SpvOpImageSampleDrefExplicitLod = 90,
107 SpvOpImageSampleProjImplicitLod = 91,
108 SpvOpImageSampleProjExplicitLod = 92,
109 SpvOpImageSampleProjDrefImplicitLod = 93,
110 SpvOpImageSampleProjDrefExplicitLod = 94,
111 SpvOpImageFetch = 95,
112 SpvOpImageGather = 96,
113 SpvOpImageDrefGather = 97,
114 SpvOpImageRead = 98,
115 SpvOpImageWrite = 99,
116 SpvOpImage = 100,
117 SpvOpImageQueryFormat = 101,
118 SpvOpImageQueryOrder = 102,
119 SpvOpImageQuerySizeLod = 103,
120 SpvOpImageQuerySize = 104,
121 SpvOpImageQueryLod = 105,
122 SpvOpImageQueryLevels = 106,
123 SpvOpImageQuerySamples = 107,
124 SpvOpConvertFToU = 109,
125 SpvOpConvertFToS = 110,
126 SpvOpConvertSToF = 111,
127 SpvOpConvertUToF = 112,
128 SpvOpUConvert = 113,
129 SpvOpSConvert = 114,
130 SpvOpFConvert = 115,
131 SpvOpQuantizeToF16 = 116,
132 SpvOpConvertPtrToU = 117,
133 SpvOpSatConvertSToU = 118,
134 SpvOpSatConvertUToS = 119,
135 SpvOpConvertUToPtr = 120,
136 SpvOpPtrCastToGeneric = 121,
137 SpvOpGenericCastToPtr = 122,
138 SpvOpGenericCastToPtrExplicit = 123,
139 SpvOpBitcast = 124,
140 SpvOpSNegate = 126,
141 SpvOpFNegate = 127,
142 SpvOpIAdd = 128,
143 SpvOpFAdd = 129,
144 SpvOpISub = 130,
145 SpvOpFSub = 131,
146 SpvOpIMul = 132,
147 SpvOpFMul = 133,
148 SpvOpUDiv = 134,
149 SpvOpSDiv = 135,
150 SpvOpFDiv = 136,
151 SpvOpUMod = 137,
152 SpvOpSRem = 138,
153 SpvOpSMod = 139,
154 SpvOpFRem = 140,
155 SpvOpFMod = 141,
156 SpvOpVectorTimesScalar = 142,
157 SpvOpMatrixTimesScalar = 143,
158 SpvOpVectorTimesMatrix = 144,
159 SpvOpMatrixTimesVector = 145,
160 SpvOpMatrixTimesMatrix = 146,
161 SpvOpOuterProduct = 147,
162 SpvOpDot = 148,
163 SpvOpIAddCarry = 149,
164 SpvOpISubBorrow = 150,
165 SpvOpUMulExtended = 151,
166 SpvOpSMulExtended = 152,
167 SpvOpAny = 154,
168 SpvOpAll = 155,
169 SpvOpIsNan = 156,
170 SpvOpIsInf = 157,
171 SpvOpIsFinite = 158,
172 SpvOpIsNormal = 159,
173 SpvOpSignBitSet = 160,
174 SpvOpLessOrGreater = 161,
175 SpvOpOrdered = 162,
176 SpvOpUnordered = 163,
177 SpvOpLogicalEqual = 164,
178 SpvOpLogicalNotEqual = 165,
179 SpvOpLogicalOr = 166,
180 SpvOpLogicalAnd = 167,
181 SpvOpLogicalNot = 168,
182 SpvOpSelect = 169,
183 SpvOpIEqual = 170,
184 SpvOpINotEqual = 171,
185 SpvOpUGreaterThan = 172,
186 SpvOpSGreaterThan = 173,
187 SpvOpUGreaterThanEqual = 174,
188 SpvOpSGreaterThanEqual = 175,
189 SpvOpULessThan = 176,
190 SpvOpSLessThan = 177,
191 SpvOpULessThanEqual = 178,
192 SpvOpSLessThanEqual = 179,
193 SpvOpFOrdEqual = 180,
194 SpvOpFUnordEqual = 181,
195 SpvOpFOrdNotEqual = 182,
196 SpvOpFUnordNotEqual = 183,
197 SpvOpFOrdLessThan = 184,
198 SpvOpFUnordLessThan = 185,
199 SpvOpFOrdGreaterThan = 186,
200 SpvOpFUnordGreaterThan = 187,
201 SpvOpFOrdLessThanEqual = 188,
202 SpvOpFUnordLessThanEqual = 189,
203 SpvOpFOrdGreaterThanEqual = 190,
204 SpvOpFUnordGreaterThanEqual = 191,
205 SpvOpShiftRightLogical = 194,
206 SpvOpShiftRightArithmetic = 195,
207 SpvOpShiftLeftLogical = 196,
208 SpvOpBitwiseOr = 197,
209 SpvOpBitwiseXor = 198,
210 SpvOpBitwiseAnd = 199,
211 SpvOpNot = 200,
212 SpvOpBitFieldInsert = 201,
213 SpvOpBitFieldSExtract = 202,
214 SpvOpBitFieldUExtract = 203,
215 SpvOpBitReverse = 204,
216 SpvOpBitCount = 205,
217 SpvOpDPdx = 207,
218 SpvOpDPdy = 208,
219 SpvOpFwidth = 209,
220 SpvOpDPdxFine = 210,
221 SpvOpDPdyFine = 211,
222 SpvOpFwidthFine = 212,
223 SpvOpDPdxCoarse = 213,
224 SpvOpDPdyCoarse = 214,
225 SpvOpFwidthCoarse = 215,
226 SpvOpEmitVertex = 218,
227 SpvOpEndPrimitive = 219,
228 SpvOpEmitStreamVertex = 220,
229 SpvOpEndStreamPrimitive = 221,
230 SpvOpControlBarrier = 224,
231 SpvOpMemoryBarrier = 225,
232 SpvOpAtomicLoad = 227,
233 SpvOpAtomicStore = 228,
234 SpvOpAtomicExchange = 229,
235 SpvOpAtomicCompareExchange = 230,
236 SpvOpAtomicCompareExchangeWeak = 231,
237 SpvOpAtomicIIncrement = 232,
238 SpvOpAtomicIDecrement = 233,
239 SpvOpAtomicIAdd = 234,
240 SpvOpAtomicISub = 235,
241 SpvOpAtomicSMin = 236,
242 SpvOpAtomicUMin = 237,
243 SpvOpAtomicSMax = 238,
244 SpvOpAtomicUMax = 239,
245 SpvOpAtomicAnd = 240,
246 SpvOpAtomicOr = 241,
247 SpvOpAtomicXor = 242,
248 SpvOpPhi = 245,
249 SpvOpLoopMerge = 246,
250 SpvOpSelectionMerge = 247,
251 SpvOpLabel = 248,
252 SpvOpBranch = 249,
253 SpvOpBranchConditional = 250,
254 SpvOpSwitch = 251,
255 SpvOpKill = 252,
256 SpvOpReturn = 253,
257 SpvOpReturnValue = 254,
258 SpvOpUnreachable = 255,
259 SpvOpLifetimeStart = 256,
260 SpvOpLifetimeStop = 257,
261 SpvOpGroupAsyncCopy = 259,
262 SpvOpGroupWaitEvents = 260,
263 SpvOpGroupAll = 261,
264 SpvOpGroupAny = 262,
265 SpvOpGroupBroadcast = 263,
266 SpvOpGroupIAdd = 264,
267 SpvOpGroupFAdd = 265,
268 SpvOpGroupFMin = 266,
269 SpvOpGroupUMin = 267,
270 SpvOpGroupSMin = 268,
271 SpvOpGroupFMax = 269,
272 SpvOpGroupUMax = 270,
273 SpvOpGroupSMax = 271,
274 SpvOpReadPipe = 274,
275 SpvOpWritePipe = 275,
276 SpvOpReservedReadPipe = 276,
277 SpvOpReservedWritePipe = 277,
278 SpvOpReserveReadPipePackets = 278,
279 SpvOpReserveWritePipePackets = 279,
280 SpvOpCommitReadPipe = 280,
281 SpvOpCommitWritePipe = 281,
282 SpvOpIsValidReserveId = 282,
283 SpvOpGetNumPipePackets = 283,
284 SpvOpGetMaxPipePackets = 284,
285 SpvOpGroupReserveReadPipePackets = 285,
286 SpvOpGroupReserveWritePipePackets = 286,
287 SpvOpGroupCommitReadPipe = 287,
288 SpvOpGroupCommitWritePipe = 288,
289 SpvOpEnqueueMarker = 291,
290 SpvOpEnqueueKernel = 292,
291 SpvOpGetKernelNDrangeSubGroupCount = 293,
292 SpvOpGetKernelNDrangeMaxSubGroupSize = 294,
293 SpvOpGetKernelWorkGroupSize = 295,
294 SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296,
295 SpvOpRetainEvent = 297,
296 SpvOpReleaseEvent = 298,
297 SpvOpCreateUserEvent = 299,
298 SpvOpIsValidEvent = 300,
299 SpvOpSetUserEventStatus = 301,
300 SpvOpCaptureEventProfilingInfo = 302,
301 SpvOpGetDefaultQueue = 303,
302 SpvOpBuildNDRange = 304,
303 SpvOpImageSparseSampleImplicitLod = 305,
304 SpvOpImageSparseSampleExplicitLod = 306,
305 SpvOpImageSparseSampleDrefImplicitLod = 307,
306 SpvOpImageSparseSampleDrefExplicitLod = 308,
307 SpvOpImageSparseSampleProjImplicitLod = 309,
308 SpvOpImageSparseSampleProjExplicitLod = 310,
309 SpvOpImageSparseSampleProjDrefImplicitLod = 311,
310 SpvOpImageSparseSampleProjDrefExplicitLod = 312,
311 SpvOpImageSparseFetch = 313,
312 SpvOpImageSparseGather = 314,
313 SpvOpImageSparseDrefGather = 315,
314 SpvOpImageSparseTexelsResident = 316,
315 SpvOpNoLine = 317,
316 SpvOpAtomicFlagTestAndSet = 318,
317 SpvOpAtomicFlagClear = 319,
318 SpvOpImageSparseRead = 320,
319 SpvOpSizeOf = 321,
320 SpvOpTypePipeStorage = 322,
321 SpvOpConstantPipeStorage = 323,
322 SpvOpCreatePipeFromPipeStorage = 324,
323 SpvOpGetKernelLocalSizeForSubgroupCount = 325,
324 SpvOpGetKernelMaxNumSubgroups = 326,
325 SpvOpTypeNamedBarrier = 327,
326 SpvOpNamedBarrierInitialize = 328,
327 SpvOpMemoryNamedBarrier = 329,
328 SpvOpModuleProcessed = 330,
329 SpvOpExecutionModeId = 331,
330 SpvOpDecorateId = 332,
331 SpvOpGroupNonUniformElect = 333,
332 SpvOpGroupNonUniformAll = 334,
333 SpvOpGroupNonUniformAny = 335,
334 SpvOpGroupNonUniformAllEqual = 336,
335 SpvOpGroupNonUniformBroadcast = 337,
336 SpvOpGroupNonUniformBroadcastFirst = 338,
337 SpvOpGroupNonUniformBallot = 339,
338 SpvOpGroupNonUniformInverseBallot = 340,
339 SpvOpGroupNonUniformBallotBitExtract = 341,
340 SpvOpGroupNonUniformBallotBitCount = 342,
341 SpvOpGroupNonUniformBallotFindLSB = 343,
342 SpvOpGroupNonUniformBallotFindMSB = 344,
343 SpvOpGroupNonUniformShuffle = 345,
344 SpvOpGroupNonUniformShuffleXor = 346,
345 SpvOpGroupNonUniformShuffleUp = 347,
346 SpvOpGroupNonUniformShuffleDown = 348,
347 SpvOpGroupNonUniformIAdd = 349,
348 SpvOpGroupNonUniformFAdd = 350,
349 SpvOpGroupNonUniformIMul = 351,
350 SpvOpGroupNonUniformFMul = 352,
351 SpvOpGroupNonUniformSMin = 353,
352 SpvOpGroupNonUniformUMin = 354,
353 SpvOpGroupNonUniformFMin = 355,
354 SpvOpGroupNonUniformSMax = 356,
355 SpvOpGroupNonUniformUMax = 357,
356 SpvOpGroupNonUniformFMax = 358,
357 SpvOpGroupNonUniformBitwiseAnd = 359,
358 SpvOpGroupNonUniformBitwiseOr = 360,
359 SpvOpGroupNonUniformBitwiseXor = 361,
360 SpvOpGroupNonUniformLogicalAnd = 362,
361 SpvOpGroupNonUniformLogicalOr = 363,
362 SpvOpGroupNonUniformLogicalXor = 364,
363 SpvOpGroupNonUniformQuadBroadcast = 365,
364 SpvOpGroupNonUniformQuadSwap = 366,
365};
366static const int kKnownOpsCount = SpvOpGroupNonUniformQuadSwap+1;
367
368
369static const char* kSpirvOpNames[] =
370{
371 "Nop",
372 "Undef",
373 "SourceContinued",
374 "Source",
375 "SourceExtension",
376 "Name",
377 "MemberName",
378 "String",
379 "Line",
380 "#9",
381 "Extension",
382 "ExtInstImport",
383 "ExtInst",
384 "VectorShuffleCompact",
385 "MemoryModel",
386 "EntryPoint",
387 "ExecutionMode",
388 "Capability",
389 "#18",
390 "TypeVoid",
391 "TypeBool",
392 "TypeInt",
393 "TypeFloat",
394 "TypeVector",
395 "TypeMatrix",
396 "TypeImage",
397 "TypeSampler",
398 "TypeSampledImage",
399 "TypeArray",
400 "TypeRuntimeArray",
401 "TypeStruct",
402 "TypeOpaque",
403 "TypePointer",
404 "TypeFunction",
405 "TypeEvent",
406 "TypeDeviceEvent",
407 "TypeReserveId",
408 "TypeQueue",
409 "TypePipe",
410 "TypeForwardPointer",
411 "#40",
412 "ConstantTrue",
413 "ConstantFalse",
414 "Constant",
415 "ConstantComposite",
416 "ConstantSampler",
417 "ConstantNull",
418 "#47",
419 "SpecConstantTrue",
420 "SpecConstantFalse",
421 "SpecConstant",
422 "SpecConstantComposite",
423 "SpecConstantOp",
424 "#53",
425 "Function",
426 "FunctionParameter",
427 "FunctionEnd",
428 "FunctionCall",
429 "#58",
430 "Variable",
431 "ImageTexelPointer",
432 "Load",
433 "Store",
434 "CopyMemory",
435 "CopyMemorySized",
436 "AccessChain",
437 "InBoundsAccessChain",
438 "PtrAccessChain",
439 "ArrayLength",
440 "GenericPtrMemSemantics",
441 "InBoundsPtrAccessChain",
442 "Decorate",
443 "MemberDecorate",
444 "DecorationGroup",
445 "GroupDecorate",
446 "GroupMemberDecorate",
447 "#76",
448 "VectorExtractDynamic",
449 "VectorInsertDynamic",
450 "VectorShuffle",
451 "CompositeConstruct",
452 "CompositeExtract",
453 "CompositeInsert",
454 "CopyObject",
455 "Transpose",
456 "#85",
457 "SampledImage",
458 "ImageSampleImplicitLod",
459 "ImageSampleExplicitLod",
460 "ImageSampleDrefImplicitLod",
461 "ImageSampleDrefExplicitLod",
462 "ImageSampleProjImplicitLod",
463 "ImageSampleProjExplicitLod",
464 "ImageSampleProjDrefImplicitLod",
465 "ImageSampleProjDrefExplicitLod",
466 "ImageFetch",
467 "ImageGather",
468 "ImageDrefGather",
469 "ImageRead",
470 "ImageWrite",
471 "Image",
472 "ImageQueryFormat",
473 "ImageQueryOrder",
474 "ImageQuerySizeLod",
475 "ImageQuerySize",
476 "ImageQueryLod",
477 "ImageQueryLevels",
478 "ImageQuerySamples",
479 "#108",
480 "ConvertFToU",
481 "ConvertFToS",
482 "ConvertSToF",
483 "ConvertUToF",
484 "UConvert",
485 "SConvert",
486 "FConvert",
487 "QuantizeToF16",
488 "ConvertPtrToU",
489 "SatConvertSToU",
490 "SatConvertUToS",
491 "ConvertUToPtr",
492 "PtrCastToGeneric",
493 "GenericCastToPtr",
494 "GenericCastToPtrExplicit",
495 "Bitcast",
496 "#125",
497 "SNegate",
498 "FNegate",
499 "IAdd",
500 "FAdd",
501 "ISub",
502 "FSub",
503 "IMul",
504 "FMul",
505 "UDiv",
506 "SDiv",
507 "FDiv",
508 "UMod",
509 "SRem",
510 "SMod",
511 "FRem",
512 "FMod",
513 "VectorTimesScalar",
514 "MatrixTimesScalar",
515 "VectorTimesMatrix",
516 "MatrixTimesVector",
517 "MatrixTimesMatrix",
518 "OuterProduct",
519 "Dot",
520 "IAddCarry",
521 "ISubBorrow",
522 "UMulExtended",
523 "SMulExtended",
524 "#153",
525 "Any",
526 "All",
527 "IsNan",
528 "IsInf",
529 "IsFinite",
530 "IsNormal",
531 "SignBitSet",
532 "LessOrGreater",
533 "Ordered",
534 "Unordered",
535 "LogicalEqual",
536 "LogicalNotEqual",
537 "LogicalOr",
538 "LogicalAnd",
539 "LogicalNot",
540 "Select",
541 "IEqual",
542 "INotEqual",
543 "UGreaterThan",
544 "SGreaterThan",
545 "UGreaterThanEqual",
546 "SGreaterThanEqual",
547 "ULessThan",
548 "SLessThan",
549 "ULessThanEqual",
550 "SLessThanEqual",
551 "FOrdEqual",
552 "FUnordEqual",
553 "FOrdNotEqual",
554 "FUnordNotEqual",
555 "FOrdLessThan",
556 "FUnordLessThan",
557 "FOrdGreaterThan",
558 "FUnordGreaterThan",
559 "FOrdLessThanEqual",
560 "FUnordLessThanEqual",
561 "FOrdGreaterThanEqual",
562 "FUnordGreaterThanEqual",
563 "#192",
564 "#193",
565 "ShiftRightLogical",
566 "ShiftRightArithmetic",
567 "ShiftLeftLogical",
568 "BitwiseOr",
569 "BitwiseXor",
570 "BitwiseAnd",
571 "Not",
572 "BitFieldInsert",
573 "BitFieldSExtract",
574 "BitFieldUExtract",
575 "BitReverse",
576 "BitCount",
577 "#206",
578 "DPdx",
579 "DPdy",
580 "Fwidth",
581 "DPdxFine",
582 "DPdyFine",
583 "FwidthFine",
584 "DPdxCoarse",
585 "DPdyCoarse",
586 "FwidthCoarse",
587 "#216",
588 "#217",
589 "EmitVertex",
590 "EndPrimitive",
591 "EmitStreamVertex",
592 "EndStreamPrimitive",
593 "#222",
594 "#223",
595 "ControlBarrier",
596 "MemoryBarrier",
597 "#226",
598 "AtomicLoad",
599 "AtomicStore",
600 "AtomicExchange",
601 "AtomicCompareExchange",
602 "AtomicCompareExchangeWeak",
603 "AtomicIIncrement",
604 "AtomicIDecrement",
605 "AtomicIAdd",
606 "AtomicISub",
607 "AtomicSMin",
608 "AtomicUMin",
609 "AtomicSMax",
610 "AtomicUMax",
611 "AtomicAnd",
612 "AtomicOr",
613 "AtomicXor",
614 "#243",
615 "#244",
616 "Phi",
617 "LoopMerge",
618 "SelectionMerge",
619 "Label",
620 "Branch",
621 "BranchConditional",
622 "Switch",
623 "Kill",
624 "Return",
625 "ReturnValue",
626 "Unreachable",
627 "LifetimeStart",
628 "LifetimeStop",
629 "#258",
630 "GroupAsyncCopy",
631 "GroupWaitEvents",
632 "GroupAll",
633 "GroupAny",
634 "GroupBroadcast",
635 "GroupIAdd",
636 "GroupFAdd",
637 "GroupFMin",
638 "GroupUMin",
639 "GroupSMin",
640 "GroupFMax",
641 "GroupUMax",
642 "GroupSMax",
643 "#272",
644 "#273",
645 "ReadPipe",
646 "WritePipe",
647 "ReservedReadPipe",
648 "ReservedWritePipe",
649 "ReserveReadPipePackets",
650 "ReserveWritePipePackets",
651 "CommitReadPipe",
652 "CommitWritePipe",
653 "IsValidReserveId",
654 "GetNumPipePackets",
655 "GetMaxPipePackets",
656 "GroupReserveReadPipePackets",
657 "GroupReserveWritePipePackets",
658 "GroupCommitReadPipe",
659 "GroupCommitWritePipe",
660 "#289",
661 "#290",
662 "EnqueueMarker",
663 "EnqueueKernel",
664 "GetKernelNDrangeSubGroupCount",
665 "GetKernelNDrangeMaxSubGroupSize",
666 "GetKernelWorkGroupSize",
667 "GetKernelPreferredWorkGroupSizeMultiple",
668 "RetainEvent",
669 "ReleaseEvent",
670 "CreateUserEvent",
671 "IsValidEvent",
672 "SetUserEventStatus",
673 "CaptureEventProfilingInfo",
674 "GetDefaultQueue",
675 "BuildNDRange",
676 "ImageSparseSampleImplicitLod",
677 "ImageSparseSampleExplicitLod",
678 "ImageSparseSampleDrefImplicitLod",
679 "ImageSparseSampleDrefExplicitLod",
680 "ImageSparseSampleProjImplicitLod",
681 "ImageSparseSampleProjExplicitLod",
682 "ImageSparseSampleProjDrefImplicitLod",
683 "ImageSparseSampleProjDrefExplicitLod",
684 "ImageSparseFetch",
685 "ImageSparseGather",
686 "ImageSparseDrefGather",
687 "ImageSparseTexelsResident",
688 "NoLine",
689 "AtomicFlagTestAndSet",
690 "AtomicFlagClear",
691 "ImageSparseRead",
692 "SizeOf",
693 "TypePipeStorage",
694 "ConstantPipeStorage",
695 "CreatePipeFromPipeStorage",
696 "GetKernelLocalSizeForSubgroupCount",
697 "GetKernelMaxNumSubgroups",
698 "TypeNamedBarrier",
699 "NamedBarrierInitialize",
700 "MemoryNamedBarrier",
701 "ModuleProcessed",
702 "ExecutionModeId",
703 "DecorateId",
704 "GroupNonUniformElect",
705 "GroupNonUniformAll",
706 "GroupNonUniformAny",
707 "GroupNonUniformAllEqual",
708 "GroupNonUniformBroadcast",
709 "GroupNonUniformBroadcastFirst",
710 "GroupNonUniformBallot",
711 "GroupNonUniformInverseBallot",
712 "GroupNonUniformBallotBitExtract",
713 "GroupNonUniformBallotBitCount",
714 "GroupNonUniformBallotFindLSB",
715 "GroupNonUniformBallotFindMSB",
716 "GroupNonUniformShuffle",
717 "GroupNonUniformShuffleXor",
718 "GroupNonUniformShuffleUp",
719 "GroupNonUniformShuffleDown",
720 "GroupNonUniformIAdd",
721 "GroupNonUniformFAdd",
722 "GroupNonUniformIMul",
723 "GroupNonUniformFMul",
724 "GroupNonUniformSMin",
725 "GroupNonUniformUMin",
726 "GroupNonUniformFMin",
727 "GroupNonUniformSMax",
728 "GroupNonUniformUMax",
729 "GroupNonUniformFMax",
730 "GroupNonUniformBitwiseAnd",
731 "GroupNonUniformBitwiseOr",
732 "GroupNonUniformBitwiseXor",
733 "GroupNonUniformLogicalAnd",
734 "GroupNonUniformLogicalOr",
735 "GroupNonUniformLogicalXor",
736 "GroupNonUniformQuadBroadcast",
737 "GroupNonUniformQuadSwap",
738};
739static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpNames) == kKnownOpsCount, "kSpirvOpNames table mismatch with known SpvOps");
740
741
742struct OpData
743{
744 uint8_t hasResult; // does it have result ID?
745 uint8_t hasType; // does it have type ID?
746 uint8_t deltaFromResult; // How many words after (optional) type+result to write out as deltas from result?
747 uint8_t varrest; // should the rest of words be written in varint encoding?
748};
749static const OpData kSpirvOpData[] =
750{
751 {0, 0, 0, 0}, // Nop
752 {1, 1, 0, 0}, // Undef
753 {0, 0, 0, 0}, // SourceContinued
754 {0, 0, 0, 1}, // Source
755 {0, 0, 0, 0}, // SourceExtension
756 {0, 0, 0, 0}, // Name
757 {0, 0, 0, 0}, // MemberName
758 {0, 0, 0, 0}, // String
759 {0, 0, 0, 1}, // Line
760 {1, 1, 0, 0}, // #9
761 {0, 0, 0, 0}, // Extension
762 {1, 0, 0, 0}, // ExtInstImport
763 {1, 1, 0, 1}, // ExtInst
764 {1, 1, 2, 1}, // VectorShuffleCompact - new in SMOLV
765 {0, 0, 0, 1}, // MemoryModel
766 {0, 0, 0, 1}, // EntryPoint
767 {0, 0, 0, 1}, // ExecutionMode
768 {0, 0, 0, 1}, // Capability
769 {1, 1, 0, 0}, // #18
770 {1, 0, 0, 1}, // TypeVoid
771 {1, 0, 0, 1}, // TypeBool
772 {1, 0, 0, 1}, // TypeInt
773 {1, 0, 0, 1}, // TypeFloat
774 {1, 0, 0, 1}, // TypeVector
775 {1, 0, 0, 1}, // TypeMatrix
776 {1, 0, 0, 1}, // TypeImage
777 {1, 0, 0, 1}, // TypeSampler
778 {1, 0, 0, 1}, // TypeSampledImage
779 {1, 0, 0, 1}, // TypeArray
780 {1, 0, 0, 1}, // TypeRuntimeArray
781 {1, 0, 0, 1}, // TypeStruct
782 {1, 0, 0, 1}, // TypeOpaque
783 {1, 0, 0, 1}, // TypePointer
784 {1, 0, 0, 1}, // TypeFunction
785 {1, 0, 0, 1}, // TypeEvent
786 {1, 0, 0, 1}, // TypeDeviceEvent
787 {1, 0, 0, 1}, // TypeReserveId
788 {1, 0, 0, 1}, // TypeQueue
789 {1, 0, 0, 1}, // TypePipe
790 {0, 0, 0, 1}, // TypeForwardPointer
791 {1, 1, 0, 0}, // #40
792 {1, 1, 0, 0}, // ConstantTrue
793 {1, 1, 0, 0}, // ConstantFalse
794 {1, 1, 0, 0}, // Constant
795 {1, 1, 9, 0}, // ConstantComposite
796 {1, 1, 0, 1}, // ConstantSampler
797 {1, 1, 0, 0}, // ConstantNull
798 {1, 1, 0, 0}, // #47
799 {1, 1, 0, 0}, // SpecConstantTrue
800 {1, 1, 0, 0}, // SpecConstantFalse
801 {1, 1, 0, 0}, // SpecConstant
802 {1, 1, 9, 0}, // SpecConstantComposite
803 {1, 1, 0, 0}, // SpecConstantOp
804 {1, 1, 0, 0}, // #53
805 {1, 1, 0, 1}, // Function
806 {1, 1, 0, 0}, // FunctionParameter
807 {0, 0, 0, 0}, // FunctionEnd
808 {1, 1, 9, 0}, // FunctionCall
809 {1, 1, 0, 0}, // #58
810 {1, 1, 0, 1}, // Variable
811 {1, 1, 0, 0}, // ImageTexelPointer
812 {1, 1, 1, 1}, // Load
813 {0, 0, 2, 1}, // Store
814 {0, 0, 0, 0}, // CopyMemory
815 {0, 0, 0, 0}, // CopyMemorySized
816 {1, 1, 0, 1}, // AccessChain
817 {1, 1, 0, 0}, // InBoundsAccessChain
818 {1, 1, 0, 0}, // PtrAccessChain
819 {1, 1, 0, 0}, // ArrayLength
820 {1, 1, 0, 0}, // GenericPtrMemSemantics
821 {1, 1, 0, 0}, // InBoundsPtrAccessChain
822 {0, 0, 0, 1}, // Decorate
823 {0, 0, 0, 1}, // MemberDecorate
824 {1, 0, 0, 0}, // DecorationGroup
825 {0, 0, 0, 0}, // GroupDecorate
826 {0, 0, 0, 0}, // GroupMemberDecorate
827 {1, 1, 0, 0}, // #76
828 {1, 1, 1, 1}, // VectorExtractDynamic
829 {1, 1, 2, 1}, // VectorInsertDynamic
830 {1, 1, 2, 1}, // VectorShuffle
831 {1, 1, 9, 0}, // CompositeConstruct
832 {1, 1, 1, 1}, // CompositeExtract
833 {1, 1, 2, 1}, // CompositeInsert
834 {1, 1, 1, 0}, // CopyObject
835 {1, 1, 0, 0}, // Transpose
836 {1, 1, 0, 0}, // #85
837 {1, 1, 0, 0}, // SampledImage
838 {1, 1, 2, 1}, // ImageSampleImplicitLod
839 {1, 1, 2, 1}, // ImageSampleExplicitLod
840 {1, 1, 3, 1}, // ImageSampleDrefImplicitLod
841 {1, 1, 3, 1}, // ImageSampleDrefExplicitLod
842 {1, 1, 2, 1}, // ImageSampleProjImplicitLod
843 {1, 1, 2, 1}, // ImageSampleProjExplicitLod
844 {1, 1, 3, 1}, // ImageSampleProjDrefImplicitLod
845 {1, 1, 3, 1}, // ImageSampleProjDrefExplicitLod
846 {1, 1, 2, 1}, // ImageFetch
847 {1, 1, 3, 1}, // ImageGather
848 {1, 1, 3, 1}, // ImageDrefGather
849 {1, 1, 2, 1}, // ImageRead
850 {0, 0, 3, 1}, // ImageWrite
851 {1, 1, 1, 0}, // Image
852 {1, 1, 1, 0}, // ImageQueryFormat
853 {1, 1, 1, 0}, // ImageQueryOrder
854 {1, 1, 2, 0}, // ImageQuerySizeLod
855 {1, 1, 1, 0}, // ImageQuerySize
856 {1, 1, 2, 0}, // ImageQueryLod
857 {1, 1, 1, 0}, // ImageQueryLevels
858 {1, 1, 1, 0}, // ImageQuerySamples
859 {1, 1, 0, 0}, // #108
860 {1, 1, 1, 0}, // ConvertFToU
861 {1, 1, 1, 0}, // ConvertFToS
862 {1, 1, 1, 0}, // ConvertSToF
863 {1, 1, 1, 0}, // ConvertUToF
864 {1, 1, 1, 0}, // UConvert
865 {1, 1, 1, 0}, // SConvert
866 {1, 1, 1, 0}, // FConvert
867 {1, 1, 1, 0}, // QuantizeToF16
868 {1, 1, 1, 0}, // ConvertPtrToU
869 {1, 1, 1, 0}, // SatConvertSToU
870 {1, 1, 1, 0}, // SatConvertUToS
871 {1, 1, 1, 0}, // ConvertUToPtr
872 {1, 1, 1, 0}, // PtrCastToGeneric
873 {1, 1, 1, 0}, // GenericCastToPtr
874 {1, 1, 1, 1}, // GenericCastToPtrExplicit
875 {1, 1, 1, 0}, // Bitcast
876 {1, 1, 0, 0}, // #125
877 {1, 1, 1, 0}, // SNegate
878 {1, 1, 1, 0}, // FNegate
879 {1, 1, 2, 0}, // IAdd
880 {1, 1, 2, 0}, // FAdd
881 {1, 1, 2, 0}, // ISub
882 {1, 1, 2, 0}, // FSub
883 {1, 1, 2, 0}, // IMul
884 {1, 1, 2, 0}, // FMul
885 {1, 1, 2, 0}, // UDiv
886 {1, 1, 2, 0}, // SDiv
887 {1, 1, 2, 0}, // FDiv
888 {1, 1, 2, 0}, // UMod
889 {1, 1, 2, 0}, // SRem
890 {1, 1, 2, 0}, // SMod
891 {1, 1, 2, 0}, // FRem
892 {1, 1, 2, 0}, // FMod
893 {1, 1, 2, 0}, // VectorTimesScalar
894 {1, 1, 2, 0}, // MatrixTimesScalar
895 {1, 1, 2, 0}, // VectorTimesMatrix
896 {1, 1, 2, 0}, // MatrixTimesVector
897 {1, 1, 2, 0}, // MatrixTimesMatrix
898 {1, 1, 2, 0}, // OuterProduct
899 {1, 1, 2, 0}, // Dot
900 {1, 1, 2, 0}, // IAddCarry
901 {1, 1, 2, 0}, // ISubBorrow
902 {1, 1, 2, 0}, // UMulExtended
903 {1, 1, 2, 0}, // SMulExtended
904 {1, 1, 0, 0}, // #153
905 {1, 1, 1, 0}, // Any
906 {1, 1, 1, 0}, // All
907 {1, 1, 1, 0}, // IsNan
908 {1, 1, 1, 0}, // IsInf
909 {1, 1, 1, 0}, // IsFinite
910 {1, 1, 1, 0}, // IsNormal
911 {1, 1, 1, 0}, // SignBitSet
912 {1, 1, 2, 0}, // LessOrGreater
913 {1, 1, 2, 0}, // Ordered
914 {1, 1, 2, 0}, // Unordered
915 {1, 1, 2, 0}, // LogicalEqual
916 {1, 1, 2, 0}, // LogicalNotEqual
917 {1, 1, 2, 0}, // LogicalOr
918 {1, 1, 2, 0}, // LogicalAnd
919 {1, 1, 1, 0}, // LogicalNot
920 {1, 1, 3, 0}, // Select
921 {1, 1, 2, 0}, // IEqual
922 {1, 1, 2, 0}, // INotEqual
923 {1, 1, 2, 0}, // UGreaterThan
924 {1, 1, 2, 0}, // SGreaterThan
925 {1, 1, 2, 0}, // UGreaterThanEqual
926 {1, 1, 2, 0}, // SGreaterThanEqual
927 {1, 1, 2, 0}, // ULessThan
928 {1, 1, 2, 0}, // SLessThan
929 {1, 1, 2, 0}, // ULessThanEqual
930 {1, 1, 2, 0}, // SLessThanEqual
931 {1, 1, 2, 0}, // FOrdEqual
932 {1, 1, 2, 0}, // FUnordEqual
933 {1, 1, 2, 0}, // FOrdNotEqual
934 {1, 1, 2, 0}, // FUnordNotEqual
935 {1, 1, 2, 0}, // FOrdLessThan
936 {1, 1, 2, 0}, // FUnordLessThan
937 {1, 1, 2, 0}, // FOrdGreaterThan
938 {1, 1, 2, 0}, // FUnordGreaterThan
939 {1, 1, 2, 0}, // FOrdLessThanEqual
940 {1, 1, 2, 0}, // FUnordLessThanEqual
941 {1, 1, 2, 0}, // FOrdGreaterThanEqual
942 {1, 1, 2, 0}, // FUnordGreaterThanEqual
943 {1, 1, 0, 0}, // #192
944 {1, 1, 0, 0}, // #193
945 {1, 1, 2, 0}, // ShiftRightLogical
946 {1, 1, 2, 0}, // ShiftRightArithmetic
947 {1, 1, 2, 0}, // ShiftLeftLogical
948 {1, 1, 2, 0}, // BitwiseOr
949 {1, 1, 2, 0}, // BitwiseXor
950 {1, 1, 2, 0}, // BitwiseAnd
951 {1, 1, 1, 0}, // Not
952 {1, 1, 4, 0}, // BitFieldInsert
953 {1, 1, 3, 0}, // BitFieldSExtract
954 {1, 1, 3, 0}, // BitFieldUExtract
955 {1, 1, 1, 0}, // BitReverse
956 {1, 1, 1, 0}, // BitCount
957 {1, 1, 0, 0}, // #206
958 {1, 1, 0, 0}, // DPdx
959 {1, 1, 0, 0}, // DPdy
960 {1, 1, 0, 0}, // Fwidth
961 {1, 1, 0, 0}, // DPdxFine
962 {1, 1, 0, 0}, // DPdyFine
963 {1, 1, 0, 0}, // FwidthFine
964 {1, 1, 0, 0}, // DPdxCoarse
965 {1, 1, 0, 0}, // DPdyCoarse
966 {1, 1, 0, 0}, // FwidthCoarse
967 {1, 1, 0, 0}, // #216
968 {1, 1, 0, 0}, // #217
969 {0, 0, 0, 0}, // EmitVertex
970 {0, 0, 0, 0}, // EndPrimitive
971 {0, 0, 0, 0}, // EmitStreamVertex
972 {0, 0, 0, 0}, // EndStreamPrimitive
973 {1, 1, 0, 0}, // #222
974 {1, 1, 0, 0}, // #223
975 {0, 0, 3, 0}, // ControlBarrier
976 {0, 0, 2, 0}, // MemoryBarrier
977 {1, 1, 0, 0}, // #226
978 {1, 1, 0, 0}, // AtomicLoad
979 {0, 0, 0, 0}, // AtomicStore
980 {1, 1, 0, 0}, // AtomicExchange
981 {1, 1, 0, 0}, // AtomicCompareExchange
982 {1, 1, 0, 0}, // AtomicCompareExchangeWeak
983 {1, 1, 0, 0}, // AtomicIIncrement
984 {1, 1, 0, 0}, // AtomicIDecrement
985 {1, 1, 0, 0}, // AtomicIAdd
986 {1, 1, 0, 0}, // AtomicISub
987 {1, 1, 0, 0}, // AtomicSMin
988 {1, 1, 0, 0}, // AtomicUMin
989 {1, 1, 0, 0}, // AtomicSMax
990 {1, 1, 0, 0}, // AtomicUMax
991 {1, 1, 0, 0}, // AtomicAnd
992 {1, 1, 0, 0}, // AtomicOr
993 {1, 1, 0, 0}, // AtomicXor
994 {1, 1, 0, 0}, // #243
995 {1, 1, 0, 0}, // #244
996 {1, 1, 0, 0}, // Phi
997 {0, 0, 2, 1}, // LoopMerge
998 {0, 0, 1, 1}, // SelectionMerge
999 {1, 0, 0, 0}, // Label
1000 {0, 0, 1, 0}, // Branch
1001 {0, 0, 3, 1}, // BranchConditional
1002 {0, 0, 0, 0}, // Switch
1003 {0, 0, 0, 0}, // Kill
1004 {0, 0, 0, 0}, // Return
1005 {0, 0, 0, 0}, // ReturnValue
1006 {0, 0, 0, 0}, // Unreachable
1007 {0, 0, 0, 0}, // LifetimeStart
1008 {0, 0, 0, 0}, // LifetimeStop
1009 {1, 1, 0, 0}, // #258
1010 {1, 1, 0, 0}, // GroupAsyncCopy
1011 {0, 0, 0, 0}, // GroupWaitEvents
1012 {1, 1, 0, 0}, // GroupAll
1013 {1, 1, 0, 0}, // GroupAny
1014 {1, 1, 0, 0}, // GroupBroadcast
1015 {1, 1, 0, 0}, // GroupIAdd
1016 {1, 1, 0, 0}, // GroupFAdd
1017 {1, 1, 0, 0}, // GroupFMin
1018 {1, 1, 0, 0}, // GroupUMin
1019 {1, 1, 0, 0}, // GroupSMin
1020 {1, 1, 0, 0}, // GroupFMax
1021 {1, 1, 0, 0}, // GroupUMax
1022 {1, 1, 0, 0}, // GroupSMax
1023 {1, 1, 0, 0}, // #272
1024 {1, 1, 0, 0}, // #273
1025 {1, 1, 0, 0}, // ReadPipe
1026 {1, 1, 0, 0}, // WritePipe
1027 {1, 1, 0, 0}, // ReservedReadPipe
1028 {1, 1, 0, 0}, // ReservedWritePipe
1029 {1, 1, 0, 0}, // ReserveReadPipePackets
1030 {1, 1, 0, 0}, // ReserveWritePipePackets
1031 {0, 0, 0, 0}, // CommitReadPipe
1032 {0, 0, 0, 0}, // CommitWritePipe
1033 {1, 1, 0, 0}, // IsValidReserveId
1034 {1, 1, 0, 0}, // GetNumPipePackets
1035 {1, 1, 0, 0}, // GetMaxPipePackets
1036 {1, 1, 0, 0}, // GroupReserveReadPipePackets
1037 {1, 1, 0, 0}, // GroupReserveWritePipePackets
1038 {0, 0, 0, 0}, // GroupCommitReadPipe
1039 {0, 0, 0, 0}, // GroupCommitWritePipe
1040 {1, 1, 0, 0}, // #289
1041 {1, 1, 0, 0}, // #290
1042 {1, 1, 0, 0}, // EnqueueMarker
1043 {1, 1, 0, 0}, // EnqueueKernel
1044 {1, 1, 0, 0}, // GetKernelNDrangeSubGroupCount
1045 {1, 1, 0, 0}, // GetKernelNDrangeMaxSubGroupSize
1046 {1, 1, 0, 0}, // GetKernelWorkGroupSize
1047 {1, 1, 0, 0}, // GetKernelPreferredWorkGroupSizeMultiple
1048 {0, 0, 0, 0}, // RetainEvent
1049 {0, 0, 0, 0}, // ReleaseEvent
1050 {1, 1, 0, 0}, // CreateUserEvent
1051 {1, 1, 0, 0}, // IsValidEvent
1052 {0, 0, 0, 0}, // SetUserEventStatus
1053 {0, 0, 0, 0}, // CaptureEventProfilingInfo
1054 {1, 1, 0, 0}, // GetDefaultQueue
1055 {1, 1, 0, 0}, // BuildNDRange
1056 {1, 1, 2, 1}, // ImageSparseSampleImplicitLod
1057 {1, 1, 2, 1}, // ImageSparseSampleExplicitLod
1058 {1, 1, 3, 1}, // ImageSparseSampleDrefImplicitLod
1059 {1, 1, 3, 1}, // ImageSparseSampleDrefExplicitLod
1060 {1, 1, 2, 1}, // ImageSparseSampleProjImplicitLod
1061 {1, 1, 2, 1}, // ImageSparseSampleProjExplicitLod
1062 {1, 1, 3, 1}, // ImageSparseSampleProjDrefImplicitLod
1063 {1, 1, 3, 1}, // ImageSparseSampleProjDrefExplicitLod
1064 {1, 1, 2, 1}, // ImageSparseFetch
1065 {1, 1, 3, 1}, // ImageSparseGather
1066 {1, 1, 3, 1}, // ImageSparseDrefGather
1067 {1, 1, 1, 0}, // ImageSparseTexelsResident
1068 {0, 0, 0, 0}, // NoLine
1069 {1, 1, 0, 0}, // AtomicFlagTestAndSet
1070 {0, 0, 0, 0}, // AtomicFlagClear
1071 {1, 1, 0, 0}, // ImageSparseRead
1072 {1, 1, 0, 0}, // SizeOf
1073 {1, 1, 0, 0}, // TypePipeStorage
1074 {1, 1, 0, 0}, // ConstantPipeStorage
1075 {1, 1, 0, 0}, // CreatePipeFromPipeStorage
1076 {1, 1, 0, 0}, // GetKernelLocalSizeForSubgroupCount
1077 {1, 1, 0, 0}, // GetKernelMaxNumSubgroups
1078 {1, 1, 0, 0}, // TypeNamedBarrier
1079 {1, 1, 0, 1}, // NamedBarrierInitialize
1080 {0, 0, 2, 1}, // MemoryNamedBarrier
1081 {1, 1, 0, 0}, // ModuleProcessed
1082 {0, 0, 0, 1}, // ExecutionModeId
1083 {0, 0, 0, 1}, // DecorateId
1084 {1, 1, 1, 1}, // GroupNonUniformElect
1085 {1, 1, 1, 1}, // GroupNonUniformAll
1086 {1, 1, 1, 1}, // GroupNonUniformAny
1087 {1, 1, 1, 1}, // GroupNonUniformAllEqual
1088 {1, 1, 1, 1}, // GroupNonUniformBroadcast
1089 {1, 1, 1, 1}, // GroupNonUniformBroadcastFirst
1090 {1, 1, 1, 1}, // GroupNonUniformBallot
1091 {1, 1, 1, 1}, // GroupNonUniformInverseBallot
1092 {1, 1, 1, 1}, // GroupNonUniformBallotBitExtract
1093 {1, 1, 1, 1}, // GroupNonUniformBallotBitCount
1094 {1, 1, 1, 1}, // GroupNonUniformBallotFindLSB
1095 {1, 1, 1, 1}, // GroupNonUniformBallotFindMSB
1096 {1, 1, 1, 1}, // GroupNonUniformShuffle
1097 {1, 1, 1, 1}, // GroupNonUniformShuffleXor
1098 {1, 1, 1, 1}, // GroupNonUniformShuffleUp
1099 {1, 1, 1, 1}, // GroupNonUniformShuffleDown
1100 {1, 1, 1, 1}, // GroupNonUniformIAdd
1101 {1, 1, 1, 1}, // GroupNonUniformFAdd
1102 {1, 1, 1, 1}, // GroupNonUniformIMul
1103 {1, 1, 1, 1}, // GroupNonUniformFMul
1104 {1, 1, 1, 1}, // GroupNonUniformSMin
1105 {1, 1, 1, 1}, // GroupNonUniformUMin
1106 {1, 1, 1, 1}, // GroupNonUniformFMin
1107 {1, 1, 1, 1}, // GroupNonUniformSMax
1108 {1, 1, 1, 1}, // GroupNonUniformUMax
1109 {1, 1, 1, 1}, // GroupNonUniformFMax
1110 {1, 1, 1, 1}, // GroupNonUniformBitwiseAnd
1111 {1, 1, 1, 1}, // GroupNonUniformBitwiseOr
1112 {1, 1, 1, 1}, // GroupNonUniformBitwiseXor
1113 {1, 1, 1, 1}, // GroupNonUniformLogicalAnd
1114 {1, 1, 1, 1}, // GroupNonUniformLogicalOr
1115 {1, 1, 1, 1}, // GroupNonUniformLogicalXor
1116 {1, 1, 1, 1}, // GroupNonUniformQuadBroadcast
1117 {1, 1, 1, 1}, // GroupNonUniformQuadSwap
1118};
1119static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpData) == kKnownOpsCount, "kSpirvOpData table mismatch with known SpvOps");
1120
1121// Instruction encoding depends on the table that describes the various SPIR-V opcodes.
1122// Whenever we change or expand the table, we need to bump up the SMOL-V version, and make
1123// sure that we can still decode files encoded by an older version.
1124static int smolv_GetKnownOpsCount(int version)
1125{
1126 if (version == 0)
1127 return SpvOpModuleProcessed+1;
1128 if (version == 1) // 2020 February, version 1 added ExecutionModeId..GroupNonUniformQuadSwap
1129 return SpvOpGroupNonUniformQuadSwap+1;
1130 return 0;
1131}
1132
1133static bool smolv_OpHasResult(SpvOp op, int opsCount)
1134{
1135 if (op < 0 || op >= opsCount)
1136 return false;
1137 return kSpirvOpData[op].hasResult != 0;
1138}
1139
1140static bool smolv_OpHasType(SpvOp op, int opsCount)
1141{
1142 if (op < 0 || op >= opsCount)
1143 return false;
1144 return kSpirvOpData[op].hasType != 0;
1145}
1146
1147static int smolv_OpDeltaFromResult(SpvOp op, int opsCount)
1148{
1149 if (op < 0 || op >= opsCount)
1150 return 0;
1151 return kSpirvOpData[op].deltaFromResult;
1152}
1153
1154static bool smolv_OpVarRest(SpvOp op, int opsCount)
1155{
1156 if (op < 0 || op >= opsCount)
1157 return false;
1158 return kSpirvOpData[op].varrest != 0;
1159}
1160
1161static bool smolv_OpDebugInfo(SpvOp op, int opsCount)
1162{
1163 return
1164 op == SpvOpSourceContinued ||
1165 op == SpvOpSource ||
1166 op == SpvOpSourceExtension ||
1167 op == SpvOpName ||
1168 op == SpvOpMemberName ||
1169 op == SpvOpString ||
1170 op == SpvOpLine ||
1171 op == SpvOpNoLine ||
1172 op == SpvOpModuleProcessed;
1173}
1174
1175
1176static int smolv_DecorationExtraOps(int dec)
1177{
1178 if (dec == 0 || (dec >= 2 && dec <= 5)) // RelaxedPrecision, Block..ColMajor
1179 return 0;
1180 if (dec >= 29 && dec <= 37) // Stream..XfbStride
1181 return 1;
1182 return -1; // unknown, encode length
1183}
1184
1185
1186// --------------------------------------------------------------------------------------------
1187
1188
1189static bool smolv_CheckGenericHeader(const uint32_t* words, size_t wordCount, uint32_t expectedMagic, uint32_t versionMask)
1190{
1191 if (!words)
1192 return false;
1193 if (wordCount < 5)
1194 return false;
1195
1196 uint32_t headerMagic = words[0];
1197 if (headerMagic != expectedMagic)
1198 return false;
1199 uint32_t headerVersion = words[1] & versionMask;
1200 if (headerVersion < 0x00010000 || headerVersion > 0x00010500)
1201 return false; // only support 1.0 through 1.5
1202
1203 return true;
1204}
1205
1206static const int kSpirVHeaderMagic = 0x07230203;
1207static const int kSmolHeaderMagic = 0x534D4F4C; // "SMOL"
1208
1209static const int kSmolCurrEncodingVersion = 1;
1210
1211static bool smolv_CheckSpirVHeader(const uint32_t* words, size_t wordCount)
1212{
1213 //@TODO: if SPIR-V header magic was reversed, that means the file got written
1214 // in a "big endian" order. Need to byteswap all words then.
1215 return smolv_CheckGenericHeader(words, wordCount, kSpirVHeaderMagic, 0xFFFFFFFF);
1216}
1217static bool smolv_CheckSmolHeader(const uint8_t* bytes, size_t byteCount)
1218{
1219 if (!smolv_CheckGenericHeader((const uint32_t*)bytes, byteCount/4, kSmolHeaderMagic, 0x00FFFFFF))
1220 return false;
1221 if (byteCount < 24) // one more word past header to store decoded length
1222 return false;
1223 // SMOL-V version
1224 int smolVersion = ((const uint32_t*)bytes)[1] >> 24;
1225 if (smolVersion < 0 || smolVersion > kSmolCurrEncodingVersion)
1226 return false;
1227 return true;
1228}
1229
1230
1231static void smolv_Write4(smolv::ByteArray& arr, uint32_t v)
1232{
1233 arr.push_back(v & 0xFF);
1234 arr.push_back((v >> 8) & 0xFF);
1235 arr.push_back((v >> 16) & 0xFF);
1236 arr.push_back(v >> 24);
1237}
1238
1239static void smolv_Write4(uint8_t*& buf, uint32_t v)
1240{
1241 memcpy(buf, &v, 4);
1242 buf += 4;
1243}
1244
1245
1246static bool smolv_Read4(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outv)
1247{
1248 if (data + 4 > dataEnd)
1249 return false;
1250 outv = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
1251 data += 4;
1252 return true;
1253}
1254
1255
1256// --------------------------------------------------------------------------------------------
1257
1258// Variable-length integer encoding for unsigned integers. In each byte:
1259// - highest bit set if more bytes follow, cleared if this is last byte.
1260// - other 7 bits are the actual value payload.
1261// Takes 1-5 bytes to encode an integer (values between 0 and 127 take one byte, etc.).
1262
1263static void smolv_WriteVarint(smolv::ByteArray& arr, uint32_t v)
1264{
1265 while (v > 127)
1266 {
1267 arr.push_back((v & 127) | 128);
1268 v >>= 7;
1269 }
1270 arr.push_back(v & 127);
1271}
1272
1273static bool smolv_ReadVarint(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outVal)
1274{
1275 uint32_t v = 0;
1276 uint32_t shift = 0;
1277 while (data < dataEnd)
1278 {
1279 uint8_t b = *data;
1280 v |= (b & 127) << shift;
1281 shift += 7;
1282 data++;
1283 if (!(b & 128))
1284 break;
1285 }
1286 outVal = v;
1287 return true; //@TODO: report failures
1288}
1289
1290static uint32_t smolv_ZigEncode(int32_t i)
1291{
1292 return (uint32_t(i) << 1) ^ (i >> 31);
1293}
1294
1295static int32_t smolv_ZigDecode(uint32_t u)
1296{
1297 return (u & 1) ? ((u >> 1) ^ ~0) : (u >> 1);
1298}
1299
1300
1301// Remap most common Op codes (Load, Store, Decorate, VectorShuffle etc.) to be in < 16 range, for
1302// more compact varint encoding. This basically swaps rarely used op values that are < 16 with the
1303// ones that are common.
1304
1305static SpvOp smolv_RemapOp(SpvOp op)
1306{
1307# define _SMOLV_SWAP_OP(op1,op2) if (op==op1) return op2; if (op==op2) return op1
1308 _SMOLV_SWAP_OP(SpvOpDecorate,SpvOpNop); // 0: 24%
1309 _SMOLV_SWAP_OP(SpvOpLoad,SpvOpUndef); // 1: 17%
1310 _SMOLV_SWAP_OP(SpvOpStore,SpvOpSourceContinued); // 2: 9%
1311 _SMOLV_SWAP_OP(SpvOpAccessChain,SpvOpSource); // 3: 7.2%
1312 _SMOLV_SWAP_OP(SpvOpVectorShuffle,SpvOpSourceExtension); // 4: 5.0%
1313 // Name - already small enum value - 5: 4.4%
1314 // MemberName - already small enum value - 6: 2.9%
1315 _SMOLV_SWAP_OP(SpvOpMemberDecorate,SpvOpString); // 7: 4.0%
1316 _SMOLV_SWAP_OP(SpvOpLabel,SpvOpLine); // 8: 0.9%
1317 _SMOLV_SWAP_OP(SpvOpVariable,(SpvOp)9); // 9: 3.9%
1318 _SMOLV_SWAP_OP(SpvOpFMul,SpvOpExtension); // 10: 3.9%
1319 _SMOLV_SWAP_OP(SpvOpFAdd,SpvOpExtInstImport); // 11: 2.5%
1320 // ExtInst - already small enum value - 12: 1.2%
1321 // VectorShuffleCompact - already small enum value - used for compact shuffle encoding
1322 _SMOLV_SWAP_OP(SpvOpTypePointer,SpvOpMemoryModel); // 14: 2.2%
1323 _SMOLV_SWAP_OP(SpvOpFNegate,SpvOpEntryPoint); // 15: 1.1%
1324# undef _SMOLV_SWAP_OP
1325 return op;
1326}
1327
1328
1329// For most compact varint encoding of common instructions, the instruction length should come out
1330// into 3 bits (be <8). SPIR-V instruction lengths are always at least 1, and for some other
1331// instructions they are guaranteed to be some other minimum length. Adjust the length before encoding,
1332// and after decoding accordingly.
1333
1334static uint32_t smolv_EncodeLen(SpvOp op, uint32_t len)
1335{
1336 len--;
1337 if (op == SpvOpVectorShuffle) len -= 4;
1338 if (op == SpvOpVectorShuffleCompact) len -= 4;
1339 if (op == SpvOpDecorate) len -= 2;
1340 if (op == SpvOpLoad) len -= 3;
1341 if (op == SpvOpAccessChain) len -= 3;
1342 return len;
1343}
1344
1345static uint32_t smolv_DecodeLen(SpvOp op, uint32_t len)
1346{
1347 len++;
1348 if (op == SpvOpVectorShuffle) len += 4;
1349 if (op == SpvOpVectorShuffleCompact) len += 4;
1350 if (op == SpvOpDecorate) len += 2;
1351 if (op == SpvOpLoad) len += 3;
1352 if (op == SpvOpAccessChain) len += 3;
1353 return len;
1354}
1355
1356
1357// Shuffling bits of length + opcode to be more compact in varint encoding in typical cases:
1358// 0x LLLL OOOO is how SPIR-V encodes it (L=length, O=op), we shuffle into:
1359// 0x LLLO OOLO, so that common case (op<16, len<8) is encoded into one byte.
1360
1361static bool smolv_WriteLengthOp(smolv::ByteArray& arr, uint32_t len, SpvOp op)
1362{
1363 len = smolv_EncodeLen(op, len);
1364 // SPIR-V length field is 16 bits; if we get a larger value that means something
1365 // was wrong, e.g. a vector shuffle instruction with less than 4 words (and our
1366 // adjustment to common lengths in smolv_EncodeLen wrapped around)
1367 if (len > 0xFFFF)
1368 return false;
1369 op = smolv_RemapOp(op);
1370 uint32_t oplen = ((len >> 4) << 20) | ((op >> 4) << 8) | ((len & 0xF) << 4) | (op & 0xF);
1371 smolv_WriteVarint(arr, oplen);
1372 return true;
1373}
1374
1375static bool smolv_ReadLengthOp(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outLen, SpvOp& outOp)
1376{
1377 uint32_t val;
1378 if (!smolv_ReadVarint(data, dataEnd, val))
1379 return false;
1380 outLen = ((val >> 20) << 4) | ((val >> 4) & 0xF);
1381 outOp = (SpvOp)(((val >> 4) & 0xFFF0) | (val & 0xF));
1382
1383 outOp = smolv_RemapOp(outOp);
1384 outLen = smolv_DecodeLen(outOp, outLen);
1385 return true;
1386}
1387
1388
1389
1390#define _SMOLV_READ_OP(len, words, op) \
1391 uint32_t len = words[0] >> 16; \
1392 if (len < 1) return false; /* malformed instruction, length needs to be at least 1 */ \
1393 if (words + len > wordsEnd) return false; /* malformed instruction, goes past end of data */ \
1394 SpvOp op = (SpvOp)(words[0] & 0xFFFF)
1395
1396
1397bool smolv::Encode(const void* spirvData, size_t spirvSize, ByteArray& outSmolv, uint32_t flags, StripOpNameFilterFunc stripFilter)
1398{
1399 const size_t wordCount = spirvSize / 4;
1400 if (wordCount * 4 != spirvSize)
1401 return false;
1402 const uint32_t* words = (const uint32_t*)spirvData;
1403 const uint32_t* wordsEnd = words + wordCount;
1404 if (!smolv_CheckSpirVHeader(words, wordCount))
1405 return false;
1406
1407 // reserve space in output (typical compression is to about 30%; reserve half of input space)
1408 outSmolv.reserve(outSmolv.size() + spirvSize/2);
1409
1410 // header (matches SPIR-V one, except different magic)
1411 smolv_Write4(outSmolv, kSmolHeaderMagic);
1412 smolv_Write4(outSmolv, (words[1] & 0x00FFFFFF) + (kSmolCurrEncodingVersion<<24)); // SPIR-V version (_XXX) + SMOL-V version (X___)
1413 smolv_Write4(outSmolv, words[2]); // generator
1414 smolv_Write4(outSmolv, words[3]); // bound
1415 smolv_Write4(outSmolv, words[4]); // schema
1416
1417 const size_t headerSpirvSizeOffset = outSmolv.size(); // size field may get updated later if stripping is enabled
1418 smolv_Write4(outSmolv, (uint32_t)spirvSize); // space needed to decode (i.e. original SPIR-V size)
1419
1420 size_t strippedSpirvWordCount = wordCount;
1421 uint32_t prevResult = 0;
1422 uint32_t prevDecorate = 0;
1423
1424 const int knownOpsCount = smolv_GetKnownOpsCount(kSmolCurrEncodingVersion);
1425
1426 words += 5;
1427 while (words < wordsEnd)
1428 {
1429 _SMOLV_READ_OP(instrLen, words, op);
1430
1431 if ((flags & kEncodeFlagStripDebugInfo) && smolv_OpDebugInfo(op, knownOpsCount))
1432 {
1433 if (!stripFilter || op != SpvOpName || !stripFilter(reinterpret_cast<const char*>(&words[2])))
1434 {
1435 strippedSpirvWordCount -= instrLen;
1436 words += instrLen;
1437 continue;
1438 }
1439 }
1440
1441 // A usual case of vector shuffle, with less than 4 components, each with a value
1442 // in [0..3] range: encode it in a more compact form, with the swizzle pattern in one byte.
1443 // Turn this into a VectorShuffleCompact instruction, that takes up unused slot in Ops.
1444 uint32_t swizzle = 0;
1445 if (op == SpvOpVectorShuffle && instrLen <= 9)
1446 {
1447 uint32_t swz0 = instrLen > 5 ? words[5] : 0;
1448 uint32_t swz1 = instrLen > 6 ? words[6] : 0;
1449 uint32_t swz2 = instrLen > 7 ? words[7] : 0;
1450 uint32_t swz3 = instrLen > 8 ? words[8] : 0;
1451 if (swz0 < 4 && swz1 < 4 && swz2 < 4 && swz3 < 4)
1452 {
1453 op = SpvOpVectorShuffleCompact;
1454 swizzle = (swz0 << 6) | (swz1 << 4) | (swz2 << 2) | (swz3);
1455 }
1456 }
1457
1458 // length + opcode
1459 if (!smolv_WriteLengthOp(outSmolv, instrLen, op))
1460 return false;
1461
1462 size_t ioffs = 1;
1463 // write type as varint, if we have it
1464 if (smolv_OpHasType(op, knownOpsCount))
1465 {
1466 if (ioffs >= instrLen)
1467 return false;
1468 smolv_WriteVarint(outSmolv, words[ioffs]);
1469 ioffs++;
1470 }
1471 // write result as delta+zig+varint, if we have it
1472 if (smolv_OpHasResult(op, knownOpsCount))
1473 {
1474 if (ioffs >= instrLen)
1475 return false;
1476 uint32_t v = words[ioffs];
1477 smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevResult)); // some deltas are negative, use zig
1478 prevResult = v;
1479 ioffs++;
1480 }
1481
1482 // Decorate & MemberDecorate: IDs relative to previous decorate
1483 if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
1484 {
1485 if (ioffs >= instrLen)
1486 return false;
1487 uint32_t v = words[ioffs];
1488 smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevDecorate)); // spirv-remapped deltas often negative, use zig
1489 prevDecorate = v;
1490 ioffs++;
1491 }
1492
1493 // MemberDecorate special encoding: whole row of MemberDecorate instructions is often referring
1494 // to the same type and linearly increasing member indices. Scan ahead to see how many we have,
1495 // and encode whole bunch as one.
1496 if (op == SpvOpMemberDecorate)
1497 {
1498 // scan ahead until we reach end, non-member-decoration or different type
1499 const uint32_t decorationType = words[ioffs-1];
1500 const uint32_t* memberWords = words;
1501 uint32_t prevIndex = 0;
1502 uint32_t prevOffset = 0;
1503 // write a byte on how many we have encoded as a bunch
1504 size_t countLocation = outSmolv.size();
1505 outSmolv.push_back(0);
1506 int count = 0;
1507 while (memberWords < wordsEnd && count < 255)
1508 {
1509 _SMOLV_READ_OP(memberLen, memberWords, memberOp);
1510 if (memberOp != SpvOpMemberDecorate)
1511 break;
1512 if (memberLen < 4)
1513 return false; // invalid input
1514 if (memberWords[1] != decorationType)
1515 break;
1516
1517 // write member index as delta from previous
1518 uint32_t memberIndex = memberWords[2];
1519 smolv_WriteVarint(outSmolv, memberIndex - prevIndex);
1520 prevIndex = memberIndex;
1521
1522 // decoration (and length if not common/known)
1523 uint32_t memberDec = memberWords[3];
1524 smolv_WriteVarint(outSmolv, memberDec);
1525 const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
1526 if (knownExtraOps == -1)
1527 smolv_WriteVarint(outSmolv, memberLen-4);
1528 else if (unsigned(knownExtraOps) + 4 != memberLen)
1529 return false; // invalid input
1530
1531 // Offset decorations are most often linearly increasing, so encode as deltas
1532 if (memberDec == 35) // Offset
1533 {
1534 if (memberLen != 5)
1535 return false;
1536 smolv_WriteVarint(outSmolv, memberWords[4]-prevOffset);
1537 prevOffset = memberWords[4];
1538 }
1539 else
1540 {
1541 // write rest of decorations as varint
1542 for (uint32_t i = 4; i < memberLen; ++i)
1543 smolv_WriteVarint(outSmolv, memberWords[i]);
1544 }
1545
1546 memberWords += memberLen;
1547 ++count;
1548 }
1549 outSmolv[countLocation] = uint8_t(count);
1550 words = memberWords;
1551 continue;
1552 }
1553
1554 // Write out this many IDs, encoding them relative+zigzag to result ID
1555 int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
1556 for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
1557 {
1558 if (ioffs >= instrLen)
1559 return false;
1560 uint32_t delta = prevResult - words[ioffs];
1561 // some deltas are negative (often on branches, or if program was processed by spirv-remap),
1562 // so use zig encoding
1563 smolv_WriteVarint(outSmolv, smolv_ZigEncode(delta));
1564 }
1565
1566 if (op == SpvOpVectorShuffleCompact)
1567 {
1568 // compact vector shuffle, just write out single swizzle byte
1569 outSmolv.push_back(uint8_t(swizzle));
1570 ioffs = instrLen;
1571 }
1572 else if (smolv_OpVarRest(op, knownOpsCount))
1573 {
1574 // write out rest of words with variable encoding (expected to be small integers)
1575 for (; ioffs < instrLen; ++ioffs)
1576 smolv_WriteVarint(outSmolv, words[ioffs]);
1577 }
1578 else
1579 {
1580 // write out rest of words without any encoding
1581 for (; ioffs < instrLen; ++ioffs)
1582 smolv_Write4(outSmolv, words[ioffs]);
1583 }
1584
1585 words += instrLen;
1586 }
1587
1588 if (strippedSpirvWordCount != wordCount)
1589 {
1590 uint8_t* headerSpirvSize = &outSmolv[headerSpirvSizeOffset];
1591 smolv_Write4(headerSpirvSize, (uint32_t)strippedSpirvWordCount * 4);
1592 }
1593
1594 return true;
1595}
1596
1597
1598size_t smolv::GetDecodedBufferSize(const void* smolvData, size_t smolvSize)
1599{
1600 if (!smolv_CheckSmolHeader((const uint8_t*)smolvData, smolvSize))
1601 return 0;
1602 const uint32_t* words = (const uint32_t*)smolvData;
1603 return words[5];
1604}
1605
1606
1607bool smolv::Decode(const void* smolvData, size_t smolvSize, void* spirvOutputBuffer, size_t spirvOutputBufferSize, uint32_t flags)
1608{
1609 // check header, and whether we have enough output buffer space
1610 const size_t neededBufferSize = GetDecodedBufferSize(smolvData, smolvSize);
1611 if (neededBufferSize == 0)
1612 return false; // invalid SMOL-V
1613 if (spirvOutputBufferSize < neededBufferSize)
1614 return false; // not enough space in output buffer
1615 if (spirvOutputBuffer == NULL)
1616 return false; // output buffer is null
1617
1618 const uint8_t* bytes = (const uint8_t*)smolvData;
1619 const uint8_t* bytesEnd = bytes + smolvSize;
1620
1621 uint8_t* outSpirv = (uint8_t*)spirvOutputBuffer;
1622
1623 uint32_t val;
1624 int smolVersion = 0;
1625
1626 // header
1627 smolv_Write4(outSpirv, kSpirVHeaderMagic); bytes += 4;
1628 smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24; val &= 0x00FFFFFF; smolv_Write4(outSpirv, val); // version
1629 smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // generator
1630 smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // bound
1631 smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // schema
1632 bytes += 4; // decode buffer size
1633
1634 // there are two SMOL-V encoding versions, both not indicating anything in their header version field:
1635 // one that is called "before zero" here (2016-08-31 code). Support decoding that one only by presence
1636 // of this special flag.
1637 const bool beforeZeroVersion = smolVersion == 0 && (flags & kDecodeFlagUse20160831AsZeroVersion) != 0;
1638
1639 const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion);
1640
1641 uint32_t prevResult = 0;
1642 uint32_t prevDecorate = 0;
1643
1644 while (bytes < bytesEnd)
1645 {
1646 // read length + opcode
1647 uint32_t instrLen;
1648 SpvOp op;
1649 if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op))
1650 return false;
1651 const bool wasSwizzle = (op == SpvOpVectorShuffleCompact);
1652 if (wasSwizzle)
1653 op = SpvOpVectorShuffle;
1654 smolv_Write4(outSpirv, (instrLen << 16) | op);
1655
1656 size_t ioffs = 1;
1657
1658 // read type as varint, if we have it
1659 if (smolv_OpHasType(op, knownOpsCount))
1660 {
1661 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1662 smolv_Write4(outSpirv, val);
1663 ioffs++;
1664 }
1665 // read result as delta+varint, if we have it
1666 if (smolv_OpHasResult(op, knownOpsCount))
1667 {
1668 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1669 val = prevResult + smolv_ZigDecode(val);
1670 smolv_Write4(outSpirv, val);
1671 prevResult = val;
1672 ioffs++;
1673 }
1674
1675 // Decorate: IDs relative to previous decorate
1676 if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
1677 {
1678 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1679 // "before zero" version did not use zig encoding for the value
1680 val = prevDecorate + (beforeZeroVersion ? val : smolv_ZigDecode(val));
1681 smolv_Write4(outSpirv, val);
1682 prevDecorate = val;
1683 ioffs++;
1684 }
1685
1686 // MemberDecorate special decoding
1687 if (op == SpvOpMemberDecorate && !beforeZeroVersion)
1688 {
1689 if (bytes >= bytesEnd)
1690 return false; // broken input
1691 int count = *bytes++;
1692 int prevIndex = 0;
1693 int prevOffset = 0;
1694 for (int m = 0; m < count; ++m)
1695 {
1696 // read member index
1697 uint32_t memberIndex;
1698 if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false;
1699 memberIndex += prevIndex;
1700 prevIndex = memberIndex;
1701
1702 // decoration (and length if not common/known)
1703 uint32_t memberDec;
1704 if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false;
1705 const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
1706 uint32_t memberLen;
1707 if (knownExtraOps == -1)
1708 {
1709 if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false;
1710 memberLen += 4;
1711 }
1712 else
1713 memberLen = 4 + knownExtraOps;
1714
1715 // write SPIR-V op+length (unless it's first member decoration, in which case it was written before)
1716 if (m != 0)
1717 {
1718 smolv_Write4(outSpirv, (memberLen << 16) | op);
1719 smolv_Write4(outSpirv, prevDecorate);
1720 }
1721 smolv_Write4(outSpirv, memberIndex);
1722 smolv_Write4(outSpirv, memberDec);
1723 // Special case for Offset decorations
1724 if (memberDec == 35) // Offset
1725 {
1726 if (memberLen != 5)
1727 return false;
1728 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1729 val += prevOffset;
1730 smolv_Write4(outSpirv, val);
1731 prevOffset = val;
1732 }
1733 else
1734 {
1735 for (uint32_t i = 4; i < memberLen; ++i)
1736 {
1737 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1738 smolv_Write4(outSpirv, val);
1739 }
1740 }
1741 }
1742 continue;
1743 }
1744
1745 // Read this many IDs, that are relative to result ID
1746 int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
1747 // "before zero" version only used zig encoding for IDs of several ops; after
1748 // that ops got zig encoding for their IDs
1749 bool zigDecodeVals = true;
1750 if (beforeZeroVersion)
1751 {
1752 if (op != SpvOpControlBarrier && op != SpvOpMemoryBarrier && op != SpvOpLoopMerge && op != SpvOpSelectionMerge && op != SpvOpBranch && op != SpvOpBranchConditional && op != SpvOpMemoryNamedBarrier)
1753 zigDecodeVals = false;
1754 }
1755 for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
1756 {
1757 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1758 if (zigDecodeVals)
1759 val = smolv_ZigDecode(val);
1760 smolv_Write4(outSpirv, prevResult - val);
1761 }
1762
1763 if (wasSwizzle && instrLen <= 9)
1764 {
1765 uint32_t swizzle = *bytes++;
1766 if (instrLen > 5) smolv_Write4(outSpirv, (swizzle >> 6) & 3);
1767 if (instrLen > 6) smolv_Write4(outSpirv, (swizzle >> 4) & 3);
1768 if (instrLen > 7) smolv_Write4(outSpirv, (swizzle >> 2) & 3);
1769 if (instrLen > 8) smolv_Write4(outSpirv, swizzle & 3);
1770 }
1771 else if (smolv_OpVarRest(op, knownOpsCount))
1772 {
1773 // read rest of words with variable encoding
1774 for (; ioffs < instrLen; ++ioffs)
1775 {
1776 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1777 smolv_Write4(outSpirv, val);
1778 }
1779 }
1780 else
1781 {
1782 // read rest of words without any encoding
1783 for (; ioffs < instrLen; ++ioffs)
1784 {
1785 if (!smolv_Read4(bytes, bytesEnd, val)) return false;
1786 smolv_Write4(outSpirv, val);
1787 }
1788 }
1789 }
1790
1791 if ((uint8_t*)spirvOutputBuffer + neededBufferSize != outSpirv)
1792 return false; // something went wrong during decoding? we should have decoded to exact output size
1793
1794 return true;
1795}
1796
1797
1798
1799// --------------------------------------------------------------------------------------------
1800// Calculating instruction count / space stats on SPIR-V and SMOL-V
1801
1802
1803struct smolv::Stats
1804{
1805 Stats() { memset(this, 0, sizeof(*this)); }
1806 size_t opCounts[kKnownOpsCount];
1807 size_t opSizes[kKnownOpsCount];
1808 size_t smolOpSizes[kKnownOpsCount];
1809 size_t varintCountsOp[6];
1810 size_t varintCountsType[6];
1811 size_t varintCountsRes[6];
1812 size_t varintCountsOther[6];
1813 size_t totalOps;
1814 size_t totalSize;
1815 size_t totalSizeSmol;
1816 size_t inputCount;
1817};
1818
1819
1820smolv::Stats* smolv::StatsCreate()
1821{
1822 return new Stats();
1823}
1824
1825void smolv::StatsDelete(smolv::Stats *s)
1826{
1827 delete s;
1828}
1829
1830
1831bool smolv::StatsCalculate(smolv::Stats* stats, const void* spirvData, size_t spirvSize)
1832{
1833 if (!stats)
1834 return false;
1835
1836 const size_t wordCount = spirvSize / 4;
1837 if (wordCount * 4 != spirvSize)
1838 return false;
1839 const uint32_t* words = (const uint32_t*)spirvData;
1840 const uint32_t* wordsEnd = words + wordCount;
1841 if (!smolv_CheckSpirVHeader(words, wordCount))
1842 return false;
1843 words += 5;
1844
1845 stats->inputCount++;
1846 stats->totalSize += wordCount;
1847
1848 while (words < wordsEnd)
1849 {
1850 _SMOLV_READ_OP(instrLen, words, op);
1851
1852 if (op < kKnownOpsCount)
1853 {
1854 stats->opCounts[op]++;
1855 stats->opSizes[op] += instrLen;
1856 }
1857 words += instrLen;
1858 stats->totalOps++;
1859 }
1860
1861 return true;
1862}
1863
1864
1865bool smolv::StatsCalculateSmol(smolv::Stats* stats, const void* smolvData, size_t smolvSize)
1866{
1867 if (!stats)
1868 return false;
1869
1870 // debugging helper to dump all encoded bytes to stdout, keep at "if 0"
1871# if 0
1872# define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() { \
1873 printf("Op %-22s ", op < kKnownOpsCount ? kSpirvOpNames[op] : "???"); \
1874 for (const uint8_t* b = instrBegin; b < bytes; ++b) \
1875 printf("%02x ", *b); \
1876 printf("\n"); \
1877 }
1878# else
1879# define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() {}
1880# endif
1881
1882 const uint8_t* bytes = (const uint8_t*)smolvData;
1883 const uint8_t* bytesEnd = bytes + smolvSize;
1884 if (!smolv_CheckSmolHeader(bytes, smolvSize))
1885 return false;
1886
1887 uint32_t val;
1888 int smolVersion;
1889 bytes += 4;
1890 smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24;
1891 const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion);
1892 bytes += 16;
1893
1894 stats->totalSizeSmol += smolvSize;
1895
1896 while (bytes < bytesEnd)
1897 {
1898 const uint8_t* instrBegin = bytes;
1899 const uint8_t* varBegin;
1900
1901 // read length + opcode
1902 uint32_t instrLen;
1903 SpvOp op;
1904 varBegin = bytes;
1905 if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op))
1906 return false;
1907 const bool wasSwizzle = (op == SpvOpVectorShuffleCompact);
1908 if (wasSwizzle)
1909 op = SpvOpVectorShuffle;
1910 stats->varintCountsOp[bytes-varBegin]++;
1911
1912 size_t ioffs = 1;
1913 if (smolv_OpHasType(op, knownOpsCount))
1914 {
1915 varBegin = bytes;
1916 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1917 stats->varintCountsType[bytes-varBegin]++;
1918 ioffs++;
1919 }
1920 if (smolv_OpHasResult(op, knownOpsCount))
1921 {
1922 varBegin = bytes;
1923 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1924 stats->varintCountsRes[bytes-varBegin]++;
1925 ioffs++;
1926 }
1927
1928 if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
1929 {
1930 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1931 ioffs++;
1932 }
1933 // MemberDecorate special decoding
1934 if (op == SpvOpMemberDecorate)
1935 {
1936 if (bytes >= bytesEnd)
1937 return false; // broken input
1938 int count = *bytes++;
1939 for (int m = 0; m < count; ++m)
1940 {
1941 uint32_t memberIndex;
1942 if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false;
1943 uint32_t memberDec;
1944 if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false;
1945 const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
1946 uint32_t memberLen;
1947 if (knownExtraOps == -1)
1948 {
1949 if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false;
1950 memberLen += 4;
1951 }
1952 else
1953 memberLen = 4 + knownExtraOps;
1954 for (uint32_t i = 4; i < memberLen; ++i)
1955 {
1956 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1957 }
1958 }
1959 stats->smolOpSizes[op] += bytes - instrBegin;
1960 _SMOLV_DEBUG_PRINT_ENCODED_BYTES();
1961 continue;
1962 }
1963
1964 int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
1965 for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
1966 {
1967 varBegin = bytes;
1968 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1969 stats->varintCountsRes[bytes-varBegin]++;
1970 }
1971
1972 if (wasSwizzle && instrLen <= 9)
1973 {
1974 bytes++;
1975 }
1976 else if (smolv_OpVarRest(op, knownOpsCount))
1977 {
1978 for (; ioffs < instrLen; ++ioffs)
1979 {
1980 varBegin = bytes;
1981 if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
1982 stats->varintCountsOther[bytes-varBegin]++;
1983 }
1984 }
1985 else
1986 {
1987 for (; ioffs < instrLen; ++ioffs)
1988 {
1989 if (!smolv_Read4(bytes, bytesEnd, val)) return false;
1990 }
1991 }
1992
1993 if (op < kKnownOpsCount)
1994 {
1995 stats->smolOpSizes[op] += bytes - instrBegin;
1996 }
1997 _SMOLV_DEBUG_PRINT_ENCODED_BYTES();
1998 }
1999
2000 return true;
2001}
2002
2003static bool CompareOpCounters (std::pair<SpvOp,size_t> a, std::pair<SpvOp,size_t> b)
2004{
2005 return a.second > b.second;
2006}
2007
2008void smolv::StatsPrint(const Stats* stats)
2009{
2010 if (!stats)
2011 return;
2012
2013 typedef std::pair<SpvOp,size_t> OpCounter;
2014 OpCounter counts[kKnownOpsCount];
2015 OpCounter sizes[kKnownOpsCount];
2016 OpCounter sizesSmol[kKnownOpsCount];
2017 for (int i = 0; i < kKnownOpsCount; ++i)
2018 {
2019 counts[i].first = (SpvOp)i;
2020 counts[i].second = stats->opCounts[i];
2021 sizes[i].first = (SpvOp)i;
2022 sizes[i].second = stats->opSizes[i];
2023 sizesSmol[i].first = (SpvOp)i;
2024 sizesSmol[i].second = stats->smolOpSizes[i];
2025 }
2026 std::sort(counts, counts + kKnownOpsCount, CompareOpCounters);
2027 std::sort(sizes, sizes + kKnownOpsCount, CompareOpCounters);
2028 std::sort(sizesSmol, sizesSmol + kKnownOpsCount, CompareOpCounters);
2029
2030 printf("Stats for %i SPIR-V inputs, total size %i words (%.1fKB):\n", (int)stats->inputCount, (int)stats->totalSize, stats->totalSize * 4.0f / 1024.0f);
2031 printf("Most occuring ops:\n");
2032 for (int i = 0; i < 30; ++i)
2033 {
2034 SpvOp op = counts[i].first;
2035 printf(" #%2i: %4i %-20s %4i (%4.1f%%)\n", i, op, kSpirvOpNames[op], (int)counts[i].second, (float)counts[i].second / (float)stats->totalOps * 100.0f);
2036 }
2037 printf("Largest total size of ops:\n");
2038 for (int i = 0; i < 30; ++i)
2039 {
2040 SpvOp op = sizes[i].first;
2041 printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n",
2042 i,
2043 kSpirvOpNames[op],
2044 (int)sizes[i].second*4,
2045 (float)sizes[i].second / (float)stats->totalSize * 100.0f,
2046 (float)sizes[i].second*4 / (float)stats->opCounts[op]
2047 );
2048 }
2049 printf("SMOL varint encoding counts per byte length:\n");
2050 printf(" B: %6s %6s %6s %6s\n", "Op", "Type", "Result", "Other");
2051 for (int i = 1; i < 6; ++i)
2052 {
2053 printf(" %i: %6i %6i %6i %6i\n", i, (int)stats->varintCountsOp[i], (int)stats->varintCountsType[i], (int)stats->varintCountsRes[i], (int)stats->varintCountsOther[i]);
2054 }
2055 printf("Largest total size of ops in SMOL:\n");
2056 for (int i = 0; i < 30; ++i)
2057 {
2058 SpvOp op = sizesSmol[i].first;
2059 printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n",
2060 i,
2061 kSpirvOpNames[op],
2062 (int)sizesSmol[i].second,
2063 (float)sizesSmol[i].second / (float)stats->totalSizeSmol * 100.0f,
2064 (float)sizesSmol[i].second / (float)stats->opCounts[op]
2065 );
2066 }
2067}
2068
2069
2070// ------------------------------------------------------------------------------
2071// This software is available under 2 licenses -- choose whichever you prefer.
2072// ------------------------------------------------------------------------------
2073// ALTERNATIVE A - MIT License
2074// Copyright (c) 2016-2020 Aras Pranckevicius
2075// Permission is hereby granted, free of charge, to any person obtaining a copy of
2076// this software and associated documentation files (the "Software"), to deal in
2077// the Software without restriction, including without limitation the rights to
2078// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
2079// of the Software, and to permit persons to whom the Software is furnished to do
2080// so, subject to the following conditions:
2081// The above copyright notice and this permission notice shall be included in all
2082// copies or substantial portions of the Software.
2083// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2084// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2085// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2086// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2087// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2088// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2089// SOFTWARE.
2090// ------------------------------------------------------------------------------
2091// ALTERNATIVE B - Public Domain (www.unlicense.org)
2092// This is free and unencumbered software released into the public domain.
2093// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
2094// software, either in source code form or as a compiled binary, for any purpose,
2095// commercial or non-commercial, and by any means.
2096// In jurisdictions that recognize copyright laws, the author or authors of this
2097// software dedicate any and all copyright interest in the software to the public
2098// domain. We make this dedication for the benefit of the public at large and to
2099// the detriment of our heirs and successors. We intend this dedication to be an
2100// overt act of relinquishment in perpetuity of all present and future rights to
2101// this software under copyright law.
2102// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2103// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2104// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2105// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2106// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2107// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2108// ------------------------------------------------------------------------------
2109