mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	
		
			
	
	
		
			2109 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			2109 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | // smol-v - public domain - https://github.com/aras-p/smol-v
 | ||
|  | // authored 2016-2020 by Aras Pranckevicius
 | ||
|  | // no warranty implied; use at your own risk
 | ||
|  | // See end of file for license information.
 | ||
|  | 
 | ||
|  | #include "smolv.h"
 | ||
|  | #include <stdint.h>
 | ||
|  | #include <vector>
 | ||
|  | #include <algorithm>
 | ||
|  | #include <cstdio>
 | ||
|  | #include <cstring>
 | ||
|  | 
 | ||
|  | #if !defined(_MSC_VER) && __cplusplus < 201103L
 | ||
|  | #define static_assert(x,y)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #define _SMOLV_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
 | ||
|  | 
 | ||
|  | // --------------------------------------------------------------------------------------------
 | ||
|  | // Metadata about known SPIR-V operations
 | ||
|  | 
 | ||
|  | enum SpvOp | ||
|  | { | ||
|  | 	SpvOpNop = 0, | ||
|  | 	SpvOpUndef = 1, | ||
|  | 	SpvOpSourceContinued = 2, | ||
|  | 	SpvOpSource = 3, | ||
|  | 	SpvOpSourceExtension = 4, | ||
|  | 	SpvOpName = 5, | ||
|  | 	SpvOpMemberName = 6, | ||
|  | 	SpvOpString = 7, | ||
|  | 	SpvOpLine = 8, | ||
|  | 	SpvOpExtension = 10, | ||
|  | 	SpvOpExtInstImport = 11, | ||
|  | 	SpvOpExtInst = 12, | ||
|  | 	SpvOpVectorShuffleCompact = 13, // not in SPIR-V, added for SMOL-V!
 | ||
|  | 	SpvOpMemoryModel = 14, | ||
|  | 	SpvOpEntryPoint = 15, | ||
|  | 	SpvOpExecutionMode = 16, | ||
|  | 	SpvOpCapability = 17, | ||
|  | 	SpvOpTypeVoid = 19, | ||
|  | 	SpvOpTypeBool = 20, | ||
|  | 	SpvOpTypeInt = 21, | ||
|  | 	SpvOpTypeFloat = 22, | ||
|  | 	SpvOpTypeVector = 23, | ||
|  | 	SpvOpTypeMatrix = 24, | ||
|  | 	SpvOpTypeImage = 25, | ||
|  | 	SpvOpTypeSampler = 26, | ||
|  | 	SpvOpTypeSampledImage = 27, | ||
|  | 	SpvOpTypeArray = 28, | ||
|  | 	SpvOpTypeRuntimeArray = 29, | ||
|  | 	SpvOpTypeStruct = 30, | ||
|  | 	SpvOpTypeOpaque = 31, | ||
|  | 	SpvOpTypePointer = 32, | ||
|  | 	SpvOpTypeFunction = 33, | ||
|  | 	SpvOpTypeEvent = 34, | ||
|  | 	SpvOpTypeDeviceEvent = 35, | ||
|  | 	SpvOpTypeReserveId = 36, | ||
|  | 	SpvOpTypeQueue = 37, | ||
|  | 	SpvOpTypePipe = 38, | ||
|  | 	SpvOpTypeForwardPointer = 39, | ||
|  | 	SpvOpConstantTrue = 41, | ||
|  | 	SpvOpConstantFalse = 42, | ||
|  | 	SpvOpConstant = 43, | ||
|  | 	SpvOpConstantComposite = 44, | ||
|  | 	SpvOpConstantSampler = 45, | ||
|  | 	SpvOpConstantNull = 46, | ||
|  | 	SpvOpSpecConstantTrue = 48, | ||
|  | 	SpvOpSpecConstantFalse = 49, | ||
|  | 	SpvOpSpecConstant = 50, | ||
|  | 	SpvOpSpecConstantComposite = 51, | ||
|  | 	SpvOpSpecConstantOp = 52, | ||
|  | 	SpvOpFunction = 54, | ||
|  | 	SpvOpFunctionParameter = 55, | ||
|  | 	SpvOpFunctionEnd = 56, | ||
|  | 	SpvOpFunctionCall = 57, | ||
|  | 	SpvOpVariable = 59, | ||
|  | 	SpvOpImageTexelPointer = 60, | ||
|  | 	SpvOpLoad = 61, | ||
|  | 	SpvOpStore = 62, | ||
|  | 	SpvOpCopyMemory = 63, | ||
|  | 	SpvOpCopyMemorySized = 64, | ||
|  | 	SpvOpAccessChain = 65, | ||
|  | 	SpvOpInBoundsAccessChain = 66, | ||
|  | 	SpvOpPtrAccessChain = 67, | ||
|  | 	SpvOpArrayLength = 68, | ||
|  | 	SpvOpGenericPtrMemSemantics = 69, | ||
|  | 	SpvOpInBoundsPtrAccessChain = 70, | ||
|  | 	SpvOpDecorate = 71, | ||
|  | 	SpvOpMemberDecorate = 72, | ||
|  | 	SpvOpDecorationGroup = 73, | ||
|  | 	SpvOpGroupDecorate = 74, | ||
|  | 	SpvOpGroupMemberDecorate = 75, | ||
|  | 	SpvOpVectorExtractDynamic = 77, | ||
|  | 	SpvOpVectorInsertDynamic = 78, | ||
|  | 	SpvOpVectorShuffle = 79, | ||
|  | 	SpvOpCompositeConstruct = 80, | ||
|  | 	SpvOpCompositeExtract = 81, | ||
|  | 	SpvOpCompositeInsert = 82, | ||
|  | 	SpvOpCopyObject = 83, | ||
|  | 	SpvOpTranspose = 84, | ||
|  | 	SpvOpSampledImage = 86, | ||
|  | 	SpvOpImageSampleImplicitLod = 87, | ||
|  | 	SpvOpImageSampleExplicitLod = 88, | ||
|  | 	SpvOpImageSampleDrefImplicitLod = 89, | ||
|  | 	SpvOpImageSampleDrefExplicitLod = 90, | ||
|  | 	SpvOpImageSampleProjImplicitLod = 91, | ||
|  | 	SpvOpImageSampleProjExplicitLod = 92, | ||
|  | 	SpvOpImageSampleProjDrefImplicitLod = 93, | ||
|  | 	SpvOpImageSampleProjDrefExplicitLod = 94, | ||
|  | 	SpvOpImageFetch = 95, | ||
|  | 	SpvOpImageGather = 96, | ||
|  | 	SpvOpImageDrefGather = 97, | ||
|  | 	SpvOpImageRead = 98, | ||
|  | 	SpvOpImageWrite = 99, | ||
|  | 	SpvOpImage = 100, | ||
|  | 	SpvOpImageQueryFormat = 101, | ||
|  | 	SpvOpImageQueryOrder = 102, | ||
|  | 	SpvOpImageQuerySizeLod = 103, | ||
|  | 	SpvOpImageQuerySize = 104, | ||
|  | 	SpvOpImageQueryLod = 105, | ||
|  | 	SpvOpImageQueryLevels = 106, | ||
|  | 	SpvOpImageQuerySamples = 107, | ||
|  | 	SpvOpConvertFToU = 109, | ||
|  | 	SpvOpConvertFToS = 110, | ||
|  | 	SpvOpConvertSToF = 111, | ||
|  | 	SpvOpConvertUToF = 112, | ||
|  | 	SpvOpUConvert = 113, | ||
|  | 	SpvOpSConvert = 114, | ||
|  | 	SpvOpFConvert = 115, | ||
|  | 	SpvOpQuantizeToF16 = 116, | ||
|  | 	SpvOpConvertPtrToU = 117, | ||
|  | 	SpvOpSatConvertSToU = 118, | ||
|  | 	SpvOpSatConvertUToS = 119, | ||
|  | 	SpvOpConvertUToPtr = 120, | ||
|  | 	SpvOpPtrCastToGeneric = 121, | ||
|  | 	SpvOpGenericCastToPtr = 122, | ||
|  | 	SpvOpGenericCastToPtrExplicit = 123, | ||
|  | 	SpvOpBitcast = 124, | ||
|  | 	SpvOpSNegate = 126, | ||
|  | 	SpvOpFNegate = 127, | ||
|  | 	SpvOpIAdd = 128, | ||
|  | 	SpvOpFAdd = 129, | ||
|  | 	SpvOpISub = 130, | ||
|  | 	SpvOpFSub = 131, | ||
|  | 	SpvOpIMul = 132, | ||
|  | 	SpvOpFMul = 133, | ||
|  | 	SpvOpUDiv = 134, | ||
|  | 	SpvOpSDiv = 135, | ||
|  | 	SpvOpFDiv = 136, | ||
|  | 	SpvOpUMod = 137, | ||
|  | 	SpvOpSRem = 138, | ||
|  | 	SpvOpSMod = 139, | ||
|  | 	SpvOpFRem = 140, | ||
|  | 	SpvOpFMod = 141, | ||
|  | 	SpvOpVectorTimesScalar = 142, | ||
|  | 	SpvOpMatrixTimesScalar = 143, | ||
|  | 	SpvOpVectorTimesMatrix = 144, | ||
|  | 	SpvOpMatrixTimesVector = 145, | ||
|  | 	SpvOpMatrixTimesMatrix = 146, | ||
|  | 	SpvOpOuterProduct = 147, | ||
|  | 	SpvOpDot = 148, | ||
|  | 	SpvOpIAddCarry = 149, | ||
|  | 	SpvOpISubBorrow = 150, | ||
|  | 	SpvOpUMulExtended = 151, | ||
|  | 	SpvOpSMulExtended = 152, | ||
|  | 	SpvOpAny = 154, | ||
|  | 	SpvOpAll = 155, | ||
|  | 	SpvOpIsNan = 156, | ||
|  | 	SpvOpIsInf = 157, | ||
|  | 	SpvOpIsFinite = 158, | ||
|  | 	SpvOpIsNormal = 159, | ||
|  | 	SpvOpSignBitSet = 160, | ||
|  | 	SpvOpLessOrGreater = 161, | ||
|  | 	SpvOpOrdered = 162, | ||
|  | 	SpvOpUnordered = 163, | ||
|  | 	SpvOpLogicalEqual = 164, | ||
|  | 	SpvOpLogicalNotEqual = 165, | ||
|  | 	SpvOpLogicalOr = 166, | ||
|  | 	SpvOpLogicalAnd = 167, | ||
|  | 	SpvOpLogicalNot = 168, | ||
|  | 	SpvOpSelect = 169, | ||
|  | 	SpvOpIEqual = 170, | ||
|  | 	SpvOpINotEqual = 171, | ||
|  | 	SpvOpUGreaterThan = 172, | ||
|  | 	SpvOpSGreaterThan = 173, | ||
|  | 	SpvOpUGreaterThanEqual = 174, | ||
|  | 	SpvOpSGreaterThanEqual = 175, | ||
|  | 	SpvOpULessThan = 176, | ||
|  | 	SpvOpSLessThan = 177, | ||
|  | 	SpvOpULessThanEqual = 178, | ||
|  | 	SpvOpSLessThanEqual = 179, | ||
|  | 	SpvOpFOrdEqual = 180, | ||
|  | 	SpvOpFUnordEqual = 181, | ||
|  | 	SpvOpFOrdNotEqual = 182, | ||
|  | 	SpvOpFUnordNotEqual = 183, | ||
|  | 	SpvOpFOrdLessThan = 184, | ||
|  | 	SpvOpFUnordLessThan = 185, | ||
|  | 	SpvOpFOrdGreaterThan = 186, | ||
|  | 	SpvOpFUnordGreaterThan = 187, | ||
|  | 	SpvOpFOrdLessThanEqual = 188, | ||
|  | 	SpvOpFUnordLessThanEqual = 189, | ||
|  | 	SpvOpFOrdGreaterThanEqual = 190, | ||
|  | 	SpvOpFUnordGreaterThanEqual = 191, | ||
|  | 	SpvOpShiftRightLogical = 194, | ||
|  | 	SpvOpShiftRightArithmetic = 195, | ||
|  | 	SpvOpShiftLeftLogical = 196, | ||
|  | 	SpvOpBitwiseOr = 197, | ||
|  | 	SpvOpBitwiseXor = 198, | ||
|  | 	SpvOpBitwiseAnd = 199, | ||
|  | 	SpvOpNot = 200, | ||
|  | 	SpvOpBitFieldInsert = 201, | ||
|  | 	SpvOpBitFieldSExtract = 202, | ||
|  | 	SpvOpBitFieldUExtract = 203, | ||
|  | 	SpvOpBitReverse = 204, | ||
|  | 	SpvOpBitCount = 205, | ||
|  | 	SpvOpDPdx = 207, | ||
|  | 	SpvOpDPdy = 208, | ||
|  | 	SpvOpFwidth = 209, | ||
|  | 	SpvOpDPdxFine = 210, | ||
|  | 	SpvOpDPdyFine = 211, | ||
|  | 	SpvOpFwidthFine = 212, | ||
|  | 	SpvOpDPdxCoarse = 213, | ||
|  | 	SpvOpDPdyCoarse = 214, | ||
|  | 	SpvOpFwidthCoarse = 215, | ||
|  | 	SpvOpEmitVertex = 218, | ||
|  | 	SpvOpEndPrimitive = 219, | ||
|  | 	SpvOpEmitStreamVertex = 220, | ||
|  | 	SpvOpEndStreamPrimitive = 221, | ||
|  | 	SpvOpControlBarrier = 224, | ||
|  | 	SpvOpMemoryBarrier = 225, | ||
|  | 	SpvOpAtomicLoad = 227, | ||
|  | 	SpvOpAtomicStore = 228, | ||
|  | 	SpvOpAtomicExchange = 229, | ||
|  | 	SpvOpAtomicCompareExchange = 230, | ||
|  | 	SpvOpAtomicCompareExchangeWeak = 231, | ||
|  | 	SpvOpAtomicIIncrement = 232, | ||
|  | 	SpvOpAtomicIDecrement = 233, | ||
|  | 	SpvOpAtomicIAdd = 234, | ||
|  | 	SpvOpAtomicISub = 235, | ||
|  | 	SpvOpAtomicSMin = 236, | ||
|  | 	SpvOpAtomicUMin = 237, | ||
|  | 	SpvOpAtomicSMax = 238, | ||
|  | 	SpvOpAtomicUMax = 239, | ||
|  | 	SpvOpAtomicAnd = 240, | ||
|  | 	SpvOpAtomicOr = 241, | ||
|  | 	SpvOpAtomicXor = 242, | ||
|  | 	SpvOpPhi = 245, | ||
|  | 	SpvOpLoopMerge = 246, | ||
|  | 	SpvOpSelectionMerge = 247, | ||
|  | 	SpvOpLabel = 248, | ||
|  | 	SpvOpBranch = 249, | ||
|  | 	SpvOpBranchConditional = 250, | ||
|  | 	SpvOpSwitch = 251, | ||
|  | 	SpvOpKill = 252, | ||
|  | 	SpvOpReturn = 253, | ||
|  | 	SpvOpReturnValue = 254, | ||
|  | 	SpvOpUnreachable = 255, | ||
|  | 	SpvOpLifetimeStart = 256, | ||
|  | 	SpvOpLifetimeStop = 257, | ||
|  | 	SpvOpGroupAsyncCopy = 259, | ||
|  | 	SpvOpGroupWaitEvents = 260, | ||
|  | 	SpvOpGroupAll = 261, | ||
|  | 	SpvOpGroupAny = 262, | ||
|  | 	SpvOpGroupBroadcast = 263, | ||
|  | 	SpvOpGroupIAdd = 264, | ||
|  | 	SpvOpGroupFAdd = 265, | ||
|  | 	SpvOpGroupFMin = 266, | ||
|  | 	SpvOpGroupUMin = 267, | ||
|  | 	SpvOpGroupSMin = 268, | ||
|  | 	SpvOpGroupFMax = 269, | ||
|  | 	SpvOpGroupUMax = 270, | ||
|  | 	SpvOpGroupSMax = 271, | ||
|  | 	SpvOpReadPipe = 274, | ||
|  | 	SpvOpWritePipe = 275, | ||
|  | 	SpvOpReservedReadPipe = 276, | ||
|  | 	SpvOpReservedWritePipe = 277, | ||
|  | 	SpvOpReserveReadPipePackets = 278, | ||
|  | 	SpvOpReserveWritePipePackets = 279, | ||
|  | 	SpvOpCommitReadPipe = 280, | ||
|  | 	SpvOpCommitWritePipe = 281, | ||
|  | 	SpvOpIsValidReserveId = 282, | ||
|  | 	SpvOpGetNumPipePackets = 283, | ||
|  | 	SpvOpGetMaxPipePackets = 284, | ||
|  | 	SpvOpGroupReserveReadPipePackets = 285, | ||
|  | 	SpvOpGroupReserveWritePipePackets = 286, | ||
|  | 	SpvOpGroupCommitReadPipe = 287, | ||
|  | 	SpvOpGroupCommitWritePipe = 288, | ||
|  | 	SpvOpEnqueueMarker = 291, | ||
|  | 	SpvOpEnqueueKernel = 292, | ||
|  | 	SpvOpGetKernelNDrangeSubGroupCount = 293, | ||
|  | 	SpvOpGetKernelNDrangeMaxSubGroupSize = 294, | ||
|  | 	SpvOpGetKernelWorkGroupSize = 295, | ||
|  | 	SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, | ||
|  | 	SpvOpRetainEvent = 297, | ||
|  | 	SpvOpReleaseEvent = 298, | ||
|  | 	SpvOpCreateUserEvent = 299, | ||
|  | 	SpvOpIsValidEvent = 300, | ||
|  | 	SpvOpSetUserEventStatus = 301, | ||
|  | 	SpvOpCaptureEventProfilingInfo = 302, | ||
|  | 	SpvOpGetDefaultQueue = 303, | ||
|  | 	SpvOpBuildNDRange = 304, | ||
|  | 	SpvOpImageSparseSampleImplicitLod = 305, | ||
|  | 	SpvOpImageSparseSampleExplicitLod = 306, | ||
|  | 	SpvOpImageSparseSampleDrefImplicitLod = 307, | ||
|  | 	SpvOpImageSparseSampleDrefExplicitLod = 308, | ||
|  | 	SpvOpImageSparseSampleProjImplicitLod = 309, | ||
|  | 	SpvOpImageSparseSampleProjExplicitLod = 310, | ||
|  | 	SpvOpImageSparseSampleProjDrefImplicitLod = 311, | ||
|  | 	SpvOpImageSparseSampleProjDrefExplicitLod = 312, | ||
|  | 	SpvOpImageSparseFetch = 313, | ||
|  | 	SpvOpImageSparseGather = 314, | ||
|  | 	SpvOpImageSparseDrefGather = 315, | ||
|  | 	SpvOpImageSparseTexelsResident = 316, | ||
|  | 	SpvOpNoLine = 317, | ||
|  | 	SpvOpAtomicFlagTestAndSet = 318, | ||
|  | 	SpvOpAtomicFlagClear = 319, | ||
|  | 	SpvOpImageSparseRead = 320, | ||
|  | 	SpvOpSizeOf = 321, | ||
|  | 	SpvOpTypePipeStorage = 322, | ||
|  | 	SpvOpConstantPipeStorage = 323, | ||
|  | 	SpvOpCreatePipeFromPipeStorage = 324, | ||
|  | 	SpvOpGetKernelLocalSizeForSubgroupCount = 325, | ||
|  | 	SpvOpGetKernelMaxNumSubgroups = 326, | ||
|  | 	SpvOpTypeNamedBarrier = 327, | ||
|  | 	SpvOpNamedBarrierInitialize = 328, | ||
|  | 	SpvOpMemoryNamedBarrier = 329, | ||
|  | 	SpvOpModuleProcessed = 330, | ||
|  | 	SpvOpExecutionModeId = 331, | ||
|  | 	SpvOpDecorateId = 332, | ||
|  | 	SpvOpGroupNonUniformElect = 333, | ||
|  | 	SpvOpGroupNonUniformAll = 334, | ||
|  | 	SpvOpGroupNonUniformAny = 335, | ||
|  | 	SpvOpGroupNonUniformAllEqual = 336, | ||
|  | 	SpvOpGroupNonUniformBroadcast = 337, | ||
|  | 	SpvOpGroupNonUniformBroadcastFirst = 338, | ||
|  | 	SpvOpGroupNonUniformBallot = 339, | ||
|  | 	SpvOpGroupNonUniformInverseBallot = 340, | ||
|  | 	SpvOpGroupNonUniformBallotBitExtract = 341, | ||
|  | 	SpvOpGroupNonUniformBallotBitCount = 342, | ||
|  | 	SpvOpGroupNonUniformBallotFindLSB = 343, | ||
|  | 	SpvOpGroupNonUniformBallotFindMSB = 344, | ||
|  | 	SpvOpGroupNonUniformShuffle = 345, | ||
|  | 	SpvOpGroupNonUniformShuffleXor = 346, | ||
|  | 	SpvOpGroupNonUniformShuffleUp = 347, | ||
|  | 	SpvOpGroupNonUniformShuffleDown = 348, | ||
|  | 	SpvOpGroupNonUniformIAdd = 349, | ||
|  | 	SpvOpGroupNonUniformFAdd = 350, | ||
|  | 	SpvOpGroupNonUniformIMul = 351, | ||
|  | 	SpvOpGroupNonUniformFMul = 352, | ||
|  | 	SpvOpGroupNonUniformSMin = 353, | ||
|  | 	SpvOpGroupNonUniformUMin = 354, | ||
|  | 	SpvOpGroupNonUniformFMin = 355, | ||
|  | 	SpvOpGroupNonUniformSMax = 356, | ||
|  | 	SpvOpGroupNonUniformUMax = 357, | ||
|  | 	SpvOpGroupNonUniformFMax = 358, | ||
|  | 	SpvOpGroupNonUniformBitwiseAnd = 359, | ||
|  | 	SpvOpGroupNonUniformBitwiseOr = 360, | ||
|  | 	SpvOpGroupNonUniformBitwiseXor = 361, | ||
|  | 	SpvOpGroupNonUniformLogicalAnd = 362, | ||
|  | 	SpvOpGroupNonUniformLogicalOr = 363, | ||
|  | 	SpvOpGroupNonUniformLogicalXor = 364, | ||
|  | 	SpvOpGroupNonUniformQuadBroadcast = 365, | ||
|  | 	SpvOpGroupNonUniformQuadSwap = 366, | ||
|  | }; | ||
|  | static const int kKnownOpsCount = SpvOpGroupNonUniformQuadSwap+1; | ||
|  | 
 | ||
|  | 
 | ||
|  | static const char* kSpirvOpNames[] = | ||
|  | { | ||
|  | 	"Nop", | ||
|  | 	"Undef", | ||
|  | 	"SourceContinued", | ||
|  | 	"Source", | ||
|  | 	"SourceExtension", | ||
|  | 	"Name", | ||
|  | 	"MemberName", | ||
|  | 	"String", | ||
|  | 	"Line", | ||
|  | 	"#9", | ||
|  | 	"Extension", | ||
|  | 	"ExtInstImport", | ||
|  | 	"ExtInst", | ||
|  | 	"VectorShuffleCompact", | ||
|  | 	"MemoryModel", | ||
|  | 	"EntryPoint", | ||
|  | 	"ExecutionMode", | ||
|  | 	"Capability", | ||
|  | 	"#18", | ||
|  | 	"TypeVoid", | ||
|  | 	"TypeBool", | ||
|  | 	"TypeInt", | ||
|  | 	"TypeFloat", | ||
|  | 	"TypeVector", | ||
|  | 	"TypeMatrix", | ||
|  | 	"TypeImage", | ||
|  | 	"TypeSampler", | ||
|  | 	"TypeSampledImage", | ||
|  | 	"TypeArray", | ||
|  | 	"TypeRuntimeArray", | ||
|  | 	"TypeStruct", | ||
|  | 	"TypeOpaque", | ||
|  | 	"TypePointer", | ||
|  | 	"TypeFunction", | ||
|  | 	"TypeEvent", | ||
|  | 	"TypeDeviceEvent", | ||
|  | 	"TypeReserveId", | ||
|  | 	"TypeQueue", | ||
|  | 	"TypePipe", | ||
|  | 	"TypeForwardPointer", | ||
|  | 	"#40", | ||
|  | 	"ConstantTrue", | ||
|  | 	"ConstantFalse", | ||
|  | 	"Constant", | ||
|  | 	"ConstantComposite", | ||
|  | 	"ConstantSampler", | ||
|  | 	"ConstantNull", | ||
|  | 	"#47", | ||
|  | 	"SpecConstantTrue", | ||
|  | 	"SpecConstantFalse", | ||
|  | 	"SpecConstant", | ||
|  | 	"SpecConstantComposite", | ||
|  | 	"SpecConstantOp", | ||
|  | 	"#53", | ||
|  | 	"Function", | ||
|  | 	"FunctionParameter", | ||
|  | 	"FunctionEnd", | ||
|  | 	"FunctionCall", | ||
|  | 	"#58", | ||
|  | 	"Variable", | ||
|  | 	"ImageTexelPointer", | ||
|  | 	"Load", | ||
|  | 	"Store", | ||
|  | 	"CopyMemory", | ||
|  | 	"CopyMemorySized", | ||
|  | 	"AccessChain", | ||
|  | 	"InBoundsAccessChain", | ||
|  | 	"PtrAccessChain", | ||
|  | 	"ArrayLength", | ||
|  | 	"GenericPtrMemSemantics", | ||
|  | 	"InBoundsPtrAccessChain", | ||
|  | 	"Decorate", | ||
|  | 	"MemberDecorate", | ||
|  | 	"DecorationGroup", | ||
|  | 	"GroupDecorate", | ||
|  | 	"GroupMemberDecorate", | ||
|  | 	"#76", | ||
|  | 	"VectorExtractDynamic", | ||
|  | 	"VectorInsertDynamic", | ||
|  | 	"VectorShuffle", | ||
|  | 	"CompositeConstruct", | ||
|  | 	"CompositeExtract", | ||
|  | 	"CompositeInsert", | ||
|  | 	"CopyObject", | ||
|  | 	"Transpose", | ||
|  | 	"#85", | ||
|  | 	"SampledImage", | ||
|  | 	"ImageSampleImplicitLod", | ||
|  | 	"ImageSampleExplicitLod", | ||
|  | 	"ImageSampleDrefImplicitLod", | ||
|  | 	"ImageSampleDrefExplicitLod", | ||
|  | 	"ImageSampleProjImplicitLod", | ||
|  | 	"ImageSampleProjExplicitLod", | ||
|  | 	"ImageSampleProjDrefImplicitLod", | ||
|  | 	"ImageSampleProjDrefExplicitLod", | ||
|  | 	"ImageFetch", | ||
|  | 	"ImageGather", | ||
|  | 	"ImageDrefGather", | ||
|  | 	"ImageRead", | ||
|  | 	"ImageWrite", | ||
|  | 	"Image", | ||
|  | 	"ImageQueryFormat", | ||
|  | 	"ImageQueryOrder", | ||
|  | 	"ImageQuerySizeLod", | ||
|  | 	"ImageQuerySize", | ||
|  | 	"ImageQueryLod", | ||
|  | 	"ImageQueryLevels", | ||
|  | 	"ImageQuerySamples", | ||
|  | 	"#108", | ||
|  | 	"ConvertFToU", | ||
|  | 	"ConvertFToS", | ||
|  | 	"ConvertSToF", | ||
|  | 	"ConvertUToF", | ||
|  | 	"UConvert", | ||
|  | 	"SConvert", | ||
|  | 	"FConvert", | ||
|  | 	"QuantizeToF16", | ||
|  | 	"ConvertPtrToU", | ||
|  | 	"SatConvertSToU", | ||
|  | 	"SatConvertUToS", | ||
|  | 	"ConvertUToPtr", | ||
|  | 	"PtrCastToGeneric", | ||
|  | 	"GenericCastToPtr", | ||
|  | 	"GenericCastToPtrExplicit", | ||
|  | 	"Bitcast", | ||
|  | 	"#125", | ||
|  | 	"SNegate", | ||
|  | 	"FNegate", | ||
|  | 	"IAdd", | ||
|  | 	"FAdd", | ||
|  | 	"ISub", | ||
|  | 	"FSub", | ||
|  | 	"IMul", | ||
|  | 	"FMul", | ||
|  | 	"UDiv", | ||
|  | 	"SDiv", | ||
|  | 	"FDiv", | ||
|  | 	"UMod", | ||
|  | 	"SRem", | ||
|  | 	"SMod", | ||
|  | 	"FRem", | ||
|  | 	"FMod", | ||
|  | 	"VectorTimesScalar", | ||
|  | 	"MatrixTimesScalar", | ||
|  | 	"VectorTimesMatrix", | ||
|  | 	"MatrixTimesVector", | ||
|  | 	"MatrixTimesMatrix", | ||
|  | 	"OuterProduct", | ||
|  | 	"Dot", | ||
|  | 	"IAddCarry", | ||
|  | 	"ISubBorrow", | ||
|  | 	"UMulExtended", | ||
|  | 	"SMulExtended", | ||
|  | 	"#153", | ||
|  | 	"Any", | ||
|  | 	"All", | ||
|  | 	"IsNan", | ||
|  | 	"IsInf", | ||
|  | 	"IsFinite", | ||
|  | 	"IsNormal", | ||
|  | 	"SignBitSet", | ||
|  | 	"LessOrGreater", | ||
|  | 	"Ordered", | ||
|  | 	"Unordered", | ||
|  | 	"LogicalEqual", | ||
|  | 	"LogicalNotEqual", | ||
|  | 	"LogicalOr", | ||
|  | 	"LogicalAnd", | ||
|  | 	"LogicalNot", | ||
|  | 	"Select", | ||
|  | 	"IEqual", | ||
|  | 	"INotEqual", | ||
|  | 	"UGreaterThan", | ||
|  | 	"SGreaterThan", | ||
|  | 	"UGreaterThanEqual", | ||
|  | 	"SGreaterThanEqual", | ||
|  | 	"ULessThan", | ||
|  | 	"SLessThan", | ||
|  | 	"ULessThanEqual", | ||
|  | 	"SLessThanEqual", | ||
|  | 	"FOrdEqual", | ||
|  | 	"FUnordEqual", | ||
|  | 	"FOrdNotEqual", | ||
|  | 	"FUnordNotEqual", | ||
|  | 	"FOrdLessThan", | ||
|  | 	"FUnordLessThan", | ||
|  | 	"FOrdGreaterThan", | ||
|  | 	"FUnordGreaterThan", | ||
|  | 	"FOrdLessThanEqual", | ||
|  | 	"FUnordLessThanEqual", | ||
|  | 	"FOrdGreaterThanEqual", | ||
|  | 	"FUnordGreaterThanEqual", | ||
|  | 	"#192", | ||
|  | 	"#193", | ||
|  | 	"ShiftRightLogical", | ||
|  | 	"ShiftRightArithmetic", | ||
|  | 	"ShiftLeftLogical", | ||
|  | 	"BitwiseOr", | ||
|  | 	"BitwiseXor", | ||
|  | 	"BitwiseAnd", | ||
|  | 	"Not", | ||
|  | 	"BitFieldInsert", | ||
|  | 	"BitFieldSExtract", | ||
|  | 	"BitFieldUExtract", | ||
|  | 	"BitReverse", | ||
|  | 	"BitCount", | ||
|  | 	"#206", | ||
|  | 	"DPdx", | ||
|  | 	"DPdy", | ||
|  | 	"Fwidth", | ||
|  | 	"DPdxFine", | ||
|  | 	"DPdyFine", | ||
|  | 	"FwidthFine", | ||
|  | 	"DPdxCoarse", | ||
|  | 	"DPdyCoarse", | ||
|  | 	"FwidthCoarse", | ||
|  | 	"#216", | ||
|  | 	"#217", | ||
|  | 	"EmitVertex", | ||
|  | 	"EndPrimitive", | ||
|  | 	"EmitStreamVertex", | ||
|  | 	"EndStreamPrimitive", | ||
|  | 	"#222", | ||
|  | 	"#223", | ||
|  | 	"ControlBarrier", | ||
|  | 	"MemoryBarrier", | ||
|  | 	"#226", | ||
|  | 	"AtomicLoad", | ||
|  | 	"AtomicStore", | ||
|  | 	"AtomicExchange", | ||
|  | 	"AtomicCompareExchange", | ||
|  | 	"AtomicCompareExchangeWeak", | ||
|  | 	"AtomicIIncrement", | ||
|  | 	"AtomicIDecrement", | ||
|  | 	"AtomicIAdd", | ||
|  | 	"AtomicISub", | ||
|  | 	"AtomicSMin", | ||
|  | 	"AtomicUMin", | ||
|  | 	"AtomicSMax", | ||
|  | 	"AtomicUMax", | ||
|  | 	"AtomicAnd", | ||
|  | 	"AtomicOr", | ||
|  | 	"AtomicXor", | ||
|  | 	"#243", | ||
|  | 	"#244", | ||
|  | 	"Phi", | ||
|  | 	"LoopMerge", | ||
|  | 	"SelectionMerge", | ||
|  | 	"Label", | ||
|  | 	"Branch", | ||
|  | 	"BranchConditional", | ||
|  | 	"Switch", | ||
|  | 	"Kill", | ||
|  | 	"Return", | ||
|  | 	"ReturnValue", | ||
|  | 	"Unreachable", | ||
|  | 	"LifetimeStart", | ||
|  | 	"LifetimeStop", | ||
|  | 	"#258", | ||
|  | 	"GroupAsyncCopy", | ||
|  | 	"GroupWaitEvents", | ||
|  | 	"GroupAll", | ||
|  | 	"GroupAny", | ||
|  | 	"GroupBroadcast", | ||
|  | 	"GroupIAdd", | ||
|  | 	"GroupFAdd", | ||
|  | 	"GroupFMin", | ||
|  | 	"GroupUMin", | ||
|  | 	"GroupSMin", | ||
|  | 	"GroupFMax", | ||
|  | 	"GroupUMax", | ||
|  | 	"GroupSMax", | ||
|  | 	"#272", | ||
|  | 	"#273", | ||
|  | 	"ReadPipe", | ||
|  | 	"WritePipe", | ||
|  | 	"ReservedReadPipe", | ||
|  | 	"ReservedWritePipe", | ||
|  | 	"ReserveReadPipePackets", | ||
|  | 	"ReserveWritePipePackets", | ||
|  | 	"CommitReadPipe", | ||
|  | 	"CommitWritePipe", | ||
|  | 	"IsValidReserveId", | ||
|  | 	"GetNumPipePackets", | ||
|  | 	"GetMaxPipePackets", | ||
|  | 	"GroupReserveReadPipePackets", | ||
|  | 	"GroupReserveWritePipePackets", | ||
|  | 	"GroupCommitReadPipe", | ||
|  | 	"GroupCommitWritePipe", | ||
|  | 	"#289", | ||
|  | 	"#290", | ||
|  | 	"EnqueueMarker", | ||
|  | 	"EnqueueKernel", | ||
|  | 	"GetKernelNDrangeSubGroupCount", | ||
|  | 	"GetKernelNDrangeMaxSubGroupSize", | ||
|  | 	"GetKernelWorkGroupSize", | ||
|  | 	"GetKernelPreferredWorkGroupSizeMultiple", | ||
|  | 	"RetainEvent", | ||
|  | 	"ReleaseEvent", | ||
|  | 	"CreateUserEvent", | ||
|  | 	"IsValidEvent", | ||
|  | 	"SetUserEventStatus", | ||
|  | 	"CaptureEventProfilingInfo", | ||
|  | 	"GetDefaultQueue", | ||
|  | 	"BuildNDRange", | ||
|  | 	"ImageSparseSampleImplicitLod", | ||
|  | 	"ImageSparseSampleExplicitLod", | ||
|  | 	"ImageSparseSampleDrefImplicitLod", | ||
|  | 	"ImageSparseSampleDrefExplicitLod", | ||
|  | 	"ImageSparseSampleProjImplicitLod", | ||
|  | 	"ImageSparseSampleProjExplicitLod", | ||
|  | 	"ImageSparseSampleProjDrefImplicitLod", | ||
|  | 	"ImageSparseSampleProjDrefExplicitLod", | ||
|  | 	"ImageSparseFetch", | ||
|  | 	"ImageSparseGather", | ||
|  | 	"ImageSparseDrefGather", | ||
|  | 	"ImageSparseTexelsResident", | ||
|  | 	"NoLine", | ||
|  | 	"AtomicFlagTestAndSet", | ||
|  | 	"AtomicFlagClear", | ||
|  | 	"ImageSparseRead", | ||
|  | 	"SizeOf", | ||
|  | 	"TypePipeStorage", | ||
|  | 	"ConstantPipeStorage", | ||
|  | 	"CreatePipeFromPipeStorage", | ||
|  | 	"GetKernelLocalSizeForSubgroupCount", | ||
|  | 	"GetKernelMaxNumSubgroups", | ||
|  | 	"TypeNamedBarrier", | ||
|  | 	"NamedBarrierInitialize", | ||
|  | 	"MemoryNamedBarrier", | ||
|  | 	"ModuleProcessed", | ||
|  | 	"ExecutionModeId", | ||
|  | 	"DecorateId", | ||
|  | 	"GroupNonUniformElect", | ||
|  | 	"GroupNonUniformAll", | ||
|  | 	"GroupNonUniformAny", | ||
|  | 	"GroupNonUniformAllEqual", | ||
|  | 	"GroupNonUniformBroadcast", | ||
|  | 	"GroupNonUniformBroadcastFirst", | ||
|  | 	"GroupNonUniformBallot", | ||
|  | 	"GroupNonUniformInverseBallot", | ||
|  | 	"GroupNonUniformBallotBitExtract", | ||
|  | 	"GroupNonUniformBallotBitCount", | ||
|  | 	"GroupNonUniformBallotFindLSB", | ||
|  | 	"GroupNonUniformBallotFindMSB", | ||
|  | 	"GroupNonUniformShuffle", | ||
|  | 	"GroupNonUniformShuffleXor", | ||
|  | 	"GroupNonUniformShuffleUp", | ||
|  | 	"GroupNonUniformShuffleDown", | ||
|  | 	"GroupNonUniformIAdd", | ||
|  | 	"GroupNonUniformFAdd", | ||
|  | 	"GroupNonUniformIMul", | ||
|  | 	"GroupNonUniformFMul", | ||
|  | 	"GroupNonUniformSMin", | ||
|  | 	"GroupNonUniformUMin", | ||
|  | 	"GroupNonUniformFMin", | ||
|  | 	"GroupNonUniformSMax", | ||
|  | 	"GroupNonUniformUMax", | ||
|  | 	"GroupNonUniformFMax", | ||
|  | 	"GroupNonUniformBitwiseAnd", | ||
|  | 	"GroupNonUniformBitwiseOr", | ||
|  | 	"GroupNonUniformBitwiseXor", | ||
|  | 	"GroupNonUniformLogicalAnd", | ||
|  | 	"GroupNonUniformLogicalOr", | ||
|  | 	"GroupNonUniformLogicalXor", | ||
|  | 	"GroupNonUniformQuadBroadcast", | ||
|  | 	"GroupNonUniformQuadSwap", | ||
|  | }; | ||
|  | static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpNames) == kKnownOpsCount, "kSpirvOpNames table mismatch with known SpvOps"); | ||
|  | 
 | ||
|  | 
 | ||
|  | struct OpData | ||
|  | { | ||
|  | 	uint8_t hasResult;	// does it have result ID?
 | ||
|  | 	uint8_t hasType;	// does it have type ID?
 | ||
|  | 	uint8_t deltaFromResult; // How many words after (optional) type+result to write out as deltas from result?
 | ||
|  | 	uint8_t varrest;	// should the rest of words be written in varint encoding?
 | ||
|  | }; | ||
|  | static const OpData kSpirvOpData[] = | ||
|  | { | ||
|  | 	{0, 0, 0, 0}, // Nop
 | ||
|  | 	{1, 1, 0, 0}, // Undef
 | ||
|  | 	{0, 0, 0, 0}, // SourceContinued
 | ||
|  | 	{0, 0, 0, 1}, // Source
 | ||
|  | 	{0, 0, 0, 0}, // SourceExtension
 | ||
|  | 	{0, 0, 0, 0}, // Name
 | ||
|  | 	{0, 0, 0, 0}, // MemberName
 | ||
|  | 	{0, 0, 0, 0}, // String
 | ||
|  | 	{0, 0, 0, 1}, // Line
 | ||
|  | 	{1, 1, 0, 0}, // #9
 | ||
|  | 	{0, 0, 0, 0}, // Extension
 | ||
|  | 	{1, 0, 0, 0}, // ExtInstImport
 | ||
|  | 	{1, 1, 0, 1}, // ExtInst
 | ||
|  | 	{1, 1, 2, 1}, // VectorShuffleCompact - new in SMOLV
 | ||
|  | 	{0, 0, 0, 1}, // MemoryModel
 | ||
|  | 	{0, 0, 0, 1}, // EntryPoint
 | ||
|  | 	{0, 0, 0, 1}, // ExecutionMode
 | ||
|  | 	{0, 0, 0, 1}, // Capability
 | ||
|  | 	{1, 1, 0, 0}, // #18
 | ||
|  | 	{1, 0, 0, 1}, // TypeVoid
 | ||
|  | 	{1, 0, 0, 1}, // TypeBool
 | ||
|  | 	{1, 0, 0, 1}, // TypeInt
 | ||
|  | 	{1, 0, 0, 1}, // TypeFloat
 | ||
|  | 	{1, 0, 0, 1}, // TypeVector
 | ||
|  | 	{1, 0, 0, 1}, // TypeMatrix
 | ||
|  | 	{1, 0, 0, 1}, // TypeImage
 | ||
|  | 	{1, 0, 0, 1}, // TypeSampler
 | ||
|  | 	{1, 0, 0, 1}, // TypeSampledImage
 | ||
|  | 	{1, 0, 0, 1}, // TypeArray
 | ||
|  | 	{1, 0, 0, 1}, // TypeRuntimeArray
 | ||
|  | 	{1, 0, 0, 1}, // TypeStruct
 | ||
|  | 	{1, 0, 0, 1}, // TypeOpaque
 | ||
|  | 	{1, 0, 0, 1}, // TypePointer
 | ||
|  | 	{1, 0, 0, 1}, // TypeFunction
 | ||
|  | 	{1, 0, 0, 1}, // TypeEvent
 | ||
|  | 	{1, 0, 0, 1}, // TypeDeviceEvent
 | ||
|  | 	{1, 0, 0, 1}, // TypeReserveId
 | ||
|  | 	{1, 0, 0, 1}, // TypeQueue
 | ||
|  | 	{1, 0, 0, 1}, // TypePipe
 | ||
|  | 	{0, 0, 0, 1}, // TypeForwardPointer
 | ||
|  | 	{1, 1, 0, 0}, // #40
 | ||
|  | 	{1, 1, 0, 0}, // ConstantTrue
 | ||
|  | 	{1, 1, 0, 0}, // ConstantFalse
 | ||
|  | 	{1, 1, 0, 0}, // Constant
 | ||
|  | 	{1, 1, 9, 0}, // ConstantComposite
 | ||
|  | 	{1, 1, 0, 1}, // ConstantSampler
 | ||
|  | 	{1, 1, 0, 0}, // ConstantNull
 | ||
|  | 	{1, 1, 0, 0}, // #47
 | ||
|  | 	{1, 1, 0, 0}, // SpecConstantTrue
 | ||
|  | 	{1, 1, 0, 0}, // SpecConstantFalse
 | ||
|  | 	{1, 1, 0, 0}, // SpecConstant
 | ||
|  | 	{1, 1, 9, 0}, // SpecConstantComposite
 | ||
|  | 	{1, 1, 0, 0}, // SpecConstantOp
 | ||
|  | 	{1, 1, 0, 0}, // #53
 | ||
|  | 	{1, 1, 0, 1}, // Function
 | ||
|  | 	{1, 1, 0, 0}, // FunctionParameter
 | ||
|  | 	{0, 0, 0, 0}, // FunctionEnd
 | ||
|  | 	{1, 1, 9, 0}, // FunctionCall
 | ||
|  | 	{1, 1, 0, 0}, // #58
 | ||
|  | 	{1, 1, 0, 1}, // Variable
 | ||
|  | 	{1, 1, 0, 0}, // ImageTexelPointer
 | ||
|  | 	{1, 1, 1, 1}, // Load
 | ||
|  | 	{0, 0, 2, 1}, // Store
 | ||
|  | 	{0, 0, 0, 0}, // CopyMemory
 | ||
|  | 	{0, 0, 0, 0}, // CopyMemorySized
 | ||
|  | 	{1, 1, 0, 1}, // AccessChain
 | ||
|  | 	{1, 1, 0, 0}, // InBoundsAccessChain
 | ||
|  | 	{1, 1, 0, 0}, // PtrAccessChain
 | ||
|  | 	{1, 1, 0, 0}, // ArrayLength
 | ||
|  | 	{1, 1, 0, 0}, // GenericPtrMemSemantics
 | ||
|  | 	{1, 1, 0, 0}, // InBoundsPtrAccessChain
 | ||
|  | 	{0, 0, 0, 1}, // Decorate
 | ||
|  | 	{0, 0, 0, 1}, // MemberDecorate
 | ||
|  | 	{1, 0, 0, 0}, // DecorationGroup
 | ||
|  | 	{0, 0, 0, 0}, // GroupDecorate
 | ||
|  | 	{0, 0, 0, 0}, // GroupMemberDecorate
 | ||
|  | 	{1, 1, 0, 0}, // #76
 | ||
|  | 	{1, 1, 1, 1}, // VectorExtractDynamic
 | ||
|  | 	{1, 1, 2, 1}, // VectorInsertDynamic
 | ||
|  | 	{1, 1, 2, 1}, // VectorShuffle
 | ||
|  | 	{1, 1, 9, 0}, // CompositeConstruct
 | ||
|  | 	{1, 1, 1, 1}, // CompositeExtract
 | ||
|  | 	{1, 1, 2, 1}, // CompositeInsert
 | ||
|  | 	{1, 1, 1, 0}, // CopyObject
 | ||
|  | 	{1, 1, 0, 0}, // Transpose
 | ||
|  | 	{1, 1, 0, 0}, // #85
 | ||
|  | 	{1, 1, 0, 0}, // SampledImage
 | ||
|  | 	{1, 1, 2, 1}, // ImageSampleImplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSampleExplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSampleDrefImplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSampleDrefExplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSampleProjImplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSampleProjExplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSampleProjDrefImplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSampleProjDrefExplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageFetch
 | ||
|  | 	{1, 1, 3, 1}, // ImageGather
 | ||
|  | 	{1, 1, 3, 1}, // ImageDrefGather
 | ||
|  | 	{1, 1, 2, 1}, // ImageRead
 | ||
|  | 	{0, 0, 3, 1}, // ImageWrite
 | ||
|  | 	{1, 1, 1, 0}, // Image
 | ||
|  | 	{1, 1, 1, 0}, // ImageQueryFormat
 | ||
|  | 	{1, 1, 1, 0}, // ImageQueryOrder
 | ||
|  | 	{1, 1, 2, 0}, // ImageQuerySizeLod
 | ||
|  | 	{1, 1, 1, 0}, // ImageQuerySize
 | ||
|  | 	{1, 1, 2, 0}, // ImageQueryLod
 | ||
|  | 	{1, 1, 1, 0}, // ImageQueryLevels
 | ||
|  | 	{1, 1, 1, 0}, // ImageQuerySamples
 | ||
|  | 	{1, 1, 0, 0}, // #108
 | ||
|  | 	{1, 1, 1, 0}, // ConvertFToU
 | ||
|  | 	{1, 1, 1, 0}, // ConvertFToS
 | ||
|  | 	{1, 1, 1, 0}, // ConvertSToF
 | ||
|  | 	{1, 1, 1, 0}, // ConvertUToF
 | ||
|  | 	{1, 1, 1, 0}, // UConvert
 | ||
|  | 	{1, 1, 1, 0}, // SConvert
 | ||
|  | 	{1, 1, 1, 0}, // FConvert
 | ||
|  | 	{1, 1, 1, 0}, // QuantizeToF16
 | ||
|  | 	{1, 1, 1, 0}, // ConvertPtrToU
 | ||
|  | 	{1, 1, 1, 0}, // SatConvertSToU
 | ||
|  | 	{1, 1, 1, 0}, // SatConvertUToS
 | ||
|  | 	{1, 1, 1, 0}, // ConvertUToPtr
 | ||
|  | 	{1, 1, 1, 0}, // PtrCastToGeneric
 | ||
|  | 	{1, 1, 1, 0}, // GenericCastToPtr
 | ||
|  | 	{1, 1, 1, 1}, // GenericCastToPtrExplicit
 | ||
|  | 	{1, 1, 1, 0}, // Bitcast
 | ||
|  | 	{1, 1, 0, 0}, // #125
 | ||
|  | 	{1, 1, 1, 0}, // SNegate
 | ||
|  | 	{1, 1, 1, 0}, // FNegate
 | ||
|  | 	{1, 1, 2, 0}, // IAdd
 | ||
|  | 	{1, 1, 2, 0}, // FAdd
 | ||
|  | 	{1, 1, 2, 0}, // ISub
 | ||
|  | 	{1, 1, 2, 0}, // FSub
 | ||
|  | 	{1, 1, 2, 0}, // IMul
 | ||
|  | 	{1, 1, 2, 0}, // FMul
 | ||
|  | 	{1, 1, 2, 0}, // UDiv
 | ||
|  | 	{1, 1, 2, 0}, // SDiv
 | ||
|  | 	{1, 1, 2, 0}, // FDiv
 | ||
|  | 	{1, 1, 2, 0}, // UMod
 | ||
|  | 	{1, 1, 2, 0}, // SRem
 | ||
|  | 	{1, 1, 2, 0}, // SMod
 | ||
|  | 	{1, 1, 2, 0}, // FRem
 | ||
|  | 	{1, 1, 2, 0}, // FMod
 | ||
|  | 	{1, 1, 2, 0}, // VectorTimesScalar
 | ||
|  | 	{1, 1, 2, 0}, // MatrixTimesScalar
 | ||
|  | 	{1, 1, 2, 0}, // VectorTimesMatrix
 | ||
|  | 	{1, 1, 2, 0}, // MatrixTimesVector
 | ||
|  | 	{1, 1, 2, 0}, // MatrixTimesMatrix
 | ||
|  | 	{1, 1, 2, 0}, // OuterProduct
 | ||
|  | 	{1, 1, 2, 0}, // Dot
 | ||
|  | 	{1, 1, 2, 0}, // IAddCarry
 | ||
|  | 	{1, 1, 2, 0}, // ISubBorrow
 | ||
|  | 	{1, 1, 2, 0}, // UMulExtended
 | ||
|  | 	{1, 1, 2, 0}, // SMulExtended
 | ||
|  | 	{1, 1, 0, 0}, // #153
 | ||
|  | 	{1, 1, 1, 0}, // Any
 | ||
|  | 	{1, 1, 1, 0}, // All
 | ||
|  | 	{1, 1, 1, 0}, // IsNan
 | ||
|  | 	{1, 1, 1, 0}, // IsInf
 | ||
|  | 	{1, 1, 1, 0}, // IsFinite
 | ||
|  | 	{1, 1, 1, 0}, // IsNormal
 | ||
|  | 	{1, 1, 1, 0}, // SignBitSet
 | ||
|  | 	{1, 1, 2, 0}, // LessOrGreater
 | ||
|  | 	{1, 1, 2, 0}, // Ordered
 | ||
|  | 	{1, 1, 2, 0}, // Unordered
 | ||
|  | 	{1, 1, 2, 0}, // LogicalEqual
 | ||
|  | 	{1, 1, 2, 0}, // LogicalNotEqual
 | ||
|  | 	{1, 1, 2, 0}, // LogicalOr
 | ||
|  | 	{1, 1, 2, 0}, // LogicalAnd
 | ||
|  | 	{1, 1, 1, 0}, // LogicalNot
 | ||
|  | 	{1, 1, 3, 0}, // Select
 | ||
|  | 	{1, 1, 2, 0}, // IEqual
 | ||
|  | 	{1, 1, 2, 0}, // INotEqual
 | ||
|  | 	{1, 1, 2, 0}, // UGreaterThan
 | ||
|  | 	{1, 1, 2, 0}, // SGreaterThan
 | ||
|  | 	{1, 1, 2, 0}, // UGreaterThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // SGreaterThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // ULessThan
 | ||
|  | 	{1, 1, 2, 0}, // SLessThan
 | ||
|  | 	{1, 1, 2, 0}, // ULessThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // SLessThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // FOrdEqual
 | ||
|  | 	{1, 1, 2, 0}, // FUnordEqual
 | ||
|  | 	{1, 1, 2, 0}, // FOrdNotEqual
 | ||
|  | 	{1, 1, 2, 0}, // FUnordNotEqual
 | ||
|  | 	{1, 1, 2, 0}, // FOrdLessThan
 | ||
|  | 	{1, 1, 2, 0}, // FUnordLessThan
 | ||
|  | 	{1, 1, 2, 0}, // FOrdGreaterThan
 | ||
|  | 	{1, 1, 2, 0}, // FUnordGreaterThan
 | ||
|  | 	{1, 1, 2, 0}, // FOrdLessThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // FUnordLessThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // FOrdGreaterThanEqual
 | ||
|  | 	{1, 1, 2, 0}, // FUnordGreaterThanEqual
 | ||
|  | 	{1, 1, 0, 0}, // #192
 | ||
|  | 	{1, 1, 0, 0}, // #193
 | ||
|  | 	{1, 1, 2, 0}, // ShiftRightLogical
 | ||
|  | 	{1, 1, 2, 0}, // ShiftRightArithmetic
 | ||
|  | 	{1, 1, 2, 0}, // ShiftLeftLogical
 | ||
|  | 	{1, 1, 2, 0}, // BitwiseOr
 | ||
|  | 	{1, 1, 2, 0}, // BitwiseXor
 | ||
|  | 	{1, 1, 2, 0}, // BitwiseAnd
 | ||
|  | 	{1, 1, 1, 0}, // Not
 | ||
|  | 	{1, 1, 4, 0}, // BitFieldInsert
 | ||
|  | 	{1, 1, 3, 0}, // BitFieldSExtract
 | ||
|  | 	{1, 1, 3, 0}, // BitFieldUExtract
 | ||
|  | 	{1, 1, 1, 0}, // BitReverse
 | ||
|  | 	{1, 1, 1, 0}, // BitCount
 | ||
|  | 	{1, 1, 0, 0}, // #206
 | ||
|  | 	{1, 1, 0, 0}, // DPdx
 | ||
|  | 	{1, 1, 0, 0}, // DPdy
 | ||
|  | 	{1, 1, 0, 0}, // Fwidth
 | ||
|  | 	{1, 1, 0, 0}, // DPdxFine
 | ||
|  | 	{1, 1, 0, 0}, // DPdyFine
 | ||
|  | 	{1, 1, 0, 0}, // FwidthFine
 | ||
|  | 	{1, 1, 0, 0}, // DPdxCoarse
 | ||
|  | 	{1, 1, 0, 0}, // DPdyCoarse
 | ||
|  | 	{1, 1, 0, 0}, // FwidthCoarse
 | ||
|  | 	{1, 1, 0, 0}, // #216
 | ||
|  | 	{1, 1, 0, 0}, // #217
 | ||
|  | 	{0, 0, 0, 0}, // EmitVertex
 | ||
|  | 	{0, 0, 0, 0}, // EndPrimitive
 | ||
|  | 	{0, 0, 0, 0}, // EmitStreamVertex
 | ||
|  | 	{0, 0, 0, 0}, // EndStreamPrimitive
 | ||
|  | 	{1, 1, 0, 0}, // #222
 | ||
|  | 	{1, 1, 0, 0}, // #223
 | ||
|  | 	{0, 0, 3, 0}, // ControlBarrier
 | ||
|  | 	{0, 0, 2, 0}, // MemoryBarrier
 | ||
|  | 	{1, 1, 0, 0}, // #226
 | ||
|  | 	{1, 1, 0, 0}, // AtomicLoad
 | ||
|  | 	{0, 0, 0, 0}, // AtomicStore
 | ||
|  | 	{1, 1, 0, 0}, // AtomicExchange
 | ||
|  | 	{1, 1, 0, 0}, // AtomicCompareExchange
 | ||
|  | 	{1, 1, 0, 0}, // AtomicCompareExchangeWeak
 | ||
|  | 	{1, 1, 0, 0}, // AtomicIIncrement
 | ||
|  | 	{1, 1, 0, 0}, // AtomicIDecrement
 | ||
|  | 	{1, 1, 0, 0}, // AtomicIAdd
 | ||
|  | 	{1, 1, 0, 0}, // AtomicISub
 | ||
|  | 	{1, 1, 0, 0}, // AtomicSMin
 | ||
|  | 	{1, 1, 0, 0}, // AtomicUMin
 | ||
|  | 	{1, 1, 0, 0}, // AtomicSMax
 | ||
|  | 	{1, 1, 0, 0}, // AtomicUMax
 | ||
|  | 	{1, 1, 0, 0}, // AtomicAnd
 | ||
|  | 	{1, 1, 0, 0}, // AtomicOr
 | ||
|  | 	{1, 1, 0, 0}, // AtomicXor
 | ||
|  | 	{1, 1, 0, 0}, // #243
 | ||
|  | 	{1, 1, 0, 0}, // #244
 | ||
|  | 	{1, 1, 0, 0}, // Phi
 | ||
|  | 	{0, 0, 2, 1}, // LoopMerge
 | ||
|  | 	{0, 0, 1, 1}, // SelectionMerge
 | ||
|  | 	{1, 0, 0, 0}, // Label
 | ||
|  | 	{0, 0, 1, 0}, // Branch
 | ||
|  | 	{0, 0, 3, 1}, // BranchConditional
 | ||
|  | 	{0, 0, 0, 0}, // Switch
 | ||
|  | 	{0, 0, 0, 0}, // Kill
 | ||
|  | 	{0, 0, 0, 0}, // Return
 | ||
|  | 	{0, 0, 0, 0}, // ReturnValue
 | ||
|  | 	{0, 0, 0, 0}, // Unreachable
 | ||
|  | 	{0, 0, 0, 0}, // LifetimeStart
 | ||
|  | 	{0, 0, 0, 0}, // LifetimeStop
 | ||
|  | 	{1, 1, 0, 0}, // #258
 | ||
|  | 	{1, 1, 0, 0}, // GroupAsyncCopy
 | ||
|  | 	{0, 0, 0, 0}, // GroupWaitEvents
 | ||
|  | 	{1, 1, 0, 0}, // GroupAll
 | ||
|  | 	{1, 1, 0, 0}, // GroupAny
 | ||
|  | 	{1, 1, 0, 0}, // GroupBroadcast
 | ||
|  | 	{1, 1, 0, 0}, // GroupIAdd
 | ||
|  | 	{1, 1, 0, 0}, // GroupFAdd
 | ||
|  | 	{1, 1, 0, 0}, // GroupFMin
 | ||
|  | 	{1, 1, 0, 0}, // GroupUMin
 | ||
|  | 	{1, 1, 0, 0}, // GroupSMin
 | ||
|  | 	{1, 1, 0, 0}, // GroupFMax
 | ||
|  | 	{1, 1, 0, 0}, // GroupUMax
 | ||
|  | 	{1, 1, 0, 0}, // GroupSMax
 | ||
|  | 	{1, 1, 0, 0}, // #272
 | ||
|  | 	{1, 1, 0, 0}, // #273
 | ||
|  | 	{1, 1, 0, 0}, // ReadPipe
 | ||
|  | 	{1, 1, 0, 0}, // WritePipe
 | ||
|  | 	{1, 1, 0, 0}, // ReservedReadPipe
 | ||
|  | 	{1, 1, 0, 0}, // ReservedWritePipe
 | ||
|  | 	{1, 1, 0, 0}, // ReserveReadPipePackets
 | ||
|  | 	{1, 1, 0, 0}, // ReserveWritePipePackets
 | ||
|  | 	{0, 0, 0, 0}, // CommitReadPipe
 | ||
|  | 	{0, 0, 0, 0}, // CommitWritePipe
 | ||
|  | 	{1, 1, 0, 0}, // IsValidReserveId
 | ||
|  | 	{1, 1, 0, 0}, // GetNumPipePackets
 | ||
|  | 	{1, 1, 0, 0}, // GetMaxPipePackets
 | ||
|  | 	{1, 1, 0, 0}, // GroupReserveReadPipePackets
 | ||
|  | 	{1, 1, 0, 0}, // GroupReserveWritePipePackets
 | ||
|  | 	{0, 0, 0, 0}, // GroupCommitReadPipe
 | ||
|  | 	{0, 0, 0, 0}, // GroupCommitWritePipe
 | ||
|  | 	{1, 1, 0, 0}, // #289
 | ||
|  | 	{1, 1, 0, 0}, // #290
 | ||
|  | 	{1, 1, 0, 0}, // EnqueueMarker
 | ||
|  | 	{1, 1, 0, 0}, // EnqueueKernel
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelNDrangeSubGroupCount
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelNDrangeMaxSubGroupSize
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelWorkGroupSize
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelPreferredWorkGroupSizeMultiple
 | ||
|  | 	{0, 0, 0, 0}, // RetainEvent
 | ||
|  | 	{0, 0, 0, 0}, // ReleaseEvent
 | ||
|  | 	{1, 1, 0, 0}, // CreateUserEvent
 | ||
|  | 	{1, 1, 0, 0}, // IsValidEvent
 | ||
|  | 	{0, 0, 0, 0}, // SetUserEventStatus
 | ||
|  | 	{0, 0, 0, 0}, // CaptureEventProfilingInfo
 | ||
|  | 	{1, 1, 0, 0}, // GetDefaultQueue
 | ||
|  | 	{1, 1, 0, 0}, // BuildNDRange
 | ||
|  | 	{1, 1, 2, 1}, // ImageSparseSampleImplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSparseSampleExplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseSampleDrefImplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseSampleDrefExplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSparseSampleProjImplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSparseSampleProjExplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseSampleProjDrefImplicitLod
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseSampleProjDrefExplicitLod
 | ||
|  | 	{1, 1, 2, 1}, // ImageSparseFetch
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseGather
 | ||
|  | 	{1, 1, 3, 1}, // ImageSparseDrefGather
 | ||
|  | 	{1, 1, 1, 0}, // ImageSparseTexelsResident
 | ||
|  | 	{0, 0, 0, 0}, // NoLine
 | ||
|  | 	{1, 1, 0, 0}, // AtomicFlagTestAndSet
 | ||
|  | 	{0, 0, 0, 0}, // AtomicFlagClear
 | ||
|  | 	{1, 1, 0, 0}, // ImageSparseRead
 | ||
|  | 	{1, 1, 0, 0}, // SizeOf
 | ||
|  | 	{1, 1, 0, 0}, // TypePipeStorage
 | ||
|  | 	{1, 1, 0, 0}, // ConstantPipeStorage
 | ||
|  | 	{1, 1, 0, 0}, // CreatePipeFromPipeStorage
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelLocalSizeForSubgroupCount
 | ||
|  | 	{1, 1, 0, 0}, // GetKernelMaxNumSubgroups
 | ||
|  | 	{1, 1, 0, 0}, // TypeNamedBarrier
 | ||
|  | 	{1, 1, 0, 1}, // NamedBarrierInitialize
 | ||
|  | 	{0, 0, 2, 1}, // MemoryNamedBarrier
 | ||
|  | 	{1, 1, 0, 0}, // ModuleProcessed
 | ||
|  | 	{0, 0, 0, 1}, // ExecutionModeId
 | ||
|  | 	{0, 0, 0, 1}, // DecorateId
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformElect
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformAll
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformAny
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformAllEqual
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBroadcast
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBroadcastFirst
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBallot
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformInverseBallot
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBallotBitExtract
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBallotBitCount
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBallotFindLSB
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBallotFindMSB
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformShuffle
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformShuffleXor
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformShuffleUp
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformShuffleDown
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformIAdd
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformFAdd
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformIMul
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformFMul
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformSMin
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformUMin
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformFMin
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformSMax
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformUMax
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformFMax
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBitwiseAnd
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBitwiseOr
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformBitwiseXor
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformLogicalAnd
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformLogicalOr
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformLogicalXor
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformQuadBroadcast
 | ||
|  | 	{1, 1, 1, 1}, // GroupNonUniformQuadSwap
 | ||
|  | }; | ||
|  | static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpData) == kKnownOpsCount, "kSpirvOpData table mismatch with known SpvOps"); | ||
|  | 
 | ||
|  | // Instruction encoding depends on the table that describes the various SPIR-V opcodes.
 | ||
|  | // Whenever we change or expand the table, we need to bump up the SMOL-V version, and make
 | ||
|  | // sure that we can still decode files encoded by an older version.
 | ||
|  | static int smolv_GetKnownOpsCount(int version) | ||
|  | { | ||
|  | 	if (version == 0) | ||
|  | 		return SpvOpModuleProcessed+1; | ||
|  | 	if (version == 1) // 2020 February, version 1 added ExecutionModeId..GroupNonUniformQuadSwap
 | ||
|  | 		return SpvOpGroupNonUniformQuadSwap+1; | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_OpHasResult(SpvOp op, int opsCount) | ||
|  | { | ||
|  | 	if (op < 0 || op >= opsCount) | ||
|  | 		return false; | ||
|  | 	return kSpirvOpData[op].hasResult != 0; | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_OpHasType(SpvOp op, int opsCount) | ||
|  | { | ||
|  | 	if (op < 0 || op >= opsCount) | ||
|  | 		return false; | ||
|  | 	return kSpirvOpData[op].hasType != 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int smolv_OpDeltaFromResult(SpvOp op, int opsCount) | ||
|  | { | ||
|  | 	if (op < 0 || op >= opsCount) | ||
|  | 		return 0; | ||
|  | 	return kSpirvOpData[op].deltaFromResult; | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_OpVarRest(SpvOp op, int opsCount) | ||
|  | { | ||
|  | 	if (op < 0 || op >= opsCount) | ||
|  | 		return false; | ||
|  | 	return kSpirvOpData[op].varrest != 0; | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_OpDebugInfo(SpvOp op, int opsCount) | ||
|  | { | ||
|  | 	return | ||
|  | 		op == SpvOpSourceContinued || | ||
|  | 		op == SpvOpSource || | ||
|  | 		op == SpvOpSourceExtension || | ||
|  | 		op == SpvOpName || | ||
|  | 		op == SpvOpMemberName || | ||
|  | 		op == SpvOpString || | ||
|  | 		op == SpvOpLine || | ||
|  | 		op == SpvOpNoLine || | ||
|  | 		op == SpvOpModuleProcessed; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static int smolv_DecorationExtraOps(int dec) | ||
|  | { | ||
|  | 	if (dec == 0 || (dec >= 2 && dec <= 5)) // RelaxedPrecision, Block..ColMajor
 | ||
|  | 		return 0; | ||
|  | 	if (dec >= 29 && dec <= 37) // Stream..XfbStride
 | ||
|  | 		return 1; | ||
|  | 	return -1; // unknown, encode length
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // --------------------------------------------------------------------------------------------
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static bool smolv_CheckGenericHeader(const uint32_t* words, size_t wordCount, uint32_t expectedMagic, uint32_t versionMask) | ||
|  | { | ||
|  | 	if (!words) | ||
|  | 		return false; | ||
|  | 	if (wordCount < 5) | ||
|  | 		return false; | ||
|  | 	 | ||
|  | 	uint32_t headerMagic = words[0]; | ||
|  | 	if (headerMagic != expectedMagic) | ||
|  | 		return false; | ||
|  | 	uint32_t headerVersion = words[1] & versionMask; | ||
|  | 	if (headerVersion < 0x00010000 || headerVersion > 0x00010500) | ||
|  | 		return false; // only support 1.0 through 1.5
 | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | static const int kSpirVHeaderMagic = 0x07230203; | ||
|  | static const int kSmolHeaderMagic = 0x534D4F4C; // "SMOL"
 | ||
|  | 
 | ||
|  | static const int kSmolCurrEncodingVersion = 1; | ||
|  | 
 | ||
|  | static bool smolv_CheckSpirVHeader(const uint32_t* words, size_t wordCount) | ||
|  | { | ||
|  | 	//@TODO: if SPIR-V header magic was reversed, that means the file got written
 | ||
|  | 	// in a "big endian" order. Need to byteswap all words then.
 | ||
|  | 	return smolv_CheckGenericHeader(words, wordCount, kSpirVHeaderMagic, 0xFFFFFFFF); | ||
|  | } | ||
|  | static bool smolv_CheckSmolHeader(const uint8_t* bytes, size_t byteCount) | ||
|  | { | ||
|  | 	if (!smolv_CheckGenericHeader((const uint32_t*)bytes, byteCount/4, kSmolHeaderMagic, 0x00FFFFFF)) | ||
|  | 		return false; | ||
|  | 	if (byteCount < 24) // one more word past header to store decoded length
 | ||
|  | 		return false; | ||
|  | 	// SMOL-V version
 | ||
|  | 	int smolVersion = ((const uint32_t*)bytes)[1] >> 24; | ||
|  | 	if (smolVersion < 0 || smolVersion > kSmolCurrEncodingVersion) | ||
|  | 		return false; | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static void smolv_Write4(smolv::ByteArray& arr, uint32_t v) | ||
|  | { | ||
|  | 	arr.push_back(v & 0xFF); | ||
|  | 	arr.push_back((v >> 8) & 0xFF); | ||
|  | 	arr.push_back((v >> 16) & 0xFF); | ||
|  | 	arr.push_back(v >> 24); | ||
|  | } | ||
|  | 
 | ||
|  | static void smolv_Write4(uint8_t*& buf, uint32_t v) | ||
|  | { | ||
|  | 	memcpy(buf, &v, 4); | ||
|  | 	buf += 4; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static bool smolv_Read4(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outv) | ||
|  | { | ||
|  | 	if (data + 4 > dataEnd) | ||
|  | 		return false; | ||
|  | 	outv = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); | ||
|  | 	data += 4; | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // --------------------------------------------------------------------------------------------
 | ||
|  | 
 | ||
|  | // Variable-length integer encoding for unsigned integers. In each byte:
 | ||
|  | // - highest bit set if more bytes follow, cleared if this is last byte.
 | ||
|  | // - other 7 bits are the actual value payload.
 | ||
|  | // Takes 1-5 bytes to encode an integer (values between 0 and 127 take one byte, etc.).
 | ||
|  | 
 | ||
|  | static void smolv_WriteVarint(smolv::ByteArray& arr, uint32_t v) | ||
|  | { | ||
|  | 	while (v > 127) | ||
|  | 	{ | ||
|  | 		arr.push_back((v & 127) | 128); | ||
|  | 		v >>= 7; | ||
|  | 	} | ||
|  | 	arr.push_back(v & 127); | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_ReadVarint(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outVal) | ||
|  | { | ||
|  | 	uint32_t v = 0; | ||
|  | 	uint32_t shift = 0; | ||
|  | 	while (data < dataEnd) | ||
|  | 	{ | ||
|  | 		uint8_t b = *data; | ||
|  | 		v |= (b & 127) << shift; | ||
|  | 		shift += 7; | ||
|  | 		data++; | ||
|  | 		if (!(b & 128)) | ||
|  | 			break; | ||
|  | 	} | ||
|  | 	outVal = v; | ||
|  | 	return true; //@TODO: report failures
 | ||
|  | } | ||
|  | 
 | ||
|  | static uint32_t smolv_ZigEncode(int32_t i) | ||
|  | { | ||
|  | 	return (uint32_t(i) << 1) ^ (i >> 31); | ||
|  | } | ||
|  | 
 | ||
|  | static int32_t smolv_ZigDecode(uint32_t u) | ||
|  | { | ||
|  | 	 return (u & 1) ? ((u >> 1) ^ ~0) : (u >> 1); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // Remap most common Op codes (Load, Store, Decorate, VectorShuffle etc.) to be in < 16 range, for
 | ||
|  | // more compact varint encoding. This basically swaps rarely used op values that are < 16 with the
 | ||
|  | // ones that are common.
 | ||
|  | 
 | ||
|  | static SpvOp smolv_RemapOp(SpvOp op) | ||
|  | { | ||
|  | #	define _SMOLV_SWAP_OP(op1,op2) if (op==op1) return op2; if (op==op2) return op1
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpDecorate,SpvOpNop); // 0: 24%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpLoad,SpvOpUndef); // 1: 17%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpStore,SpvOpSourceContinued); // 2: 9%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpAccessChain,SpvOpSource); // 3: 7.2%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpVectorShuffle,SpvOpSourceExtension); // 4: 5.0%
 | ||
|  | 	// Name - already small enum value - 5: 4.4%
 | ||
|  | 	// MemberName - already small enum value - 6: 2.9%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpMemberDecorate,SpvOpString); // 7: 4.0%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpLabel,SpvOpLine); // 8: 0.9%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpVariable,(SpvOp)9); // 9: 3.9%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpFMul,SpvOpExtension); // 10: 3.9%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpFAdd,SpvOpExtInstImport); // 11: 2.5%
 | ||
|  | 	// ExtInst - already small enum value - 12: 1.2%
 | ||
|  | 	// VectorShuffleCompact - already small enum value - used for compact shuffle encoding
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpTypePointer,SpvOpMemoryModel); // 14: 2.2%
 | ||
|  | 	_SMOLV_SWAP_OP(SpvOpFNegate,SpvOpEntryPoint); // 15: 1.1%
 | ||
|  | #	undef _SMOLV_SWAP_OP
 | ||
|  | 	return op; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // For most compact varint encoding of common instructions, the instruction length should come out
 | ||
|  | // into 3 bits (be <8). SPIR-V instruction lengths are always at least 1, and for some other
 | ||
|  | // instructions they are guaranteed to be some other minimum length. Adjust the length before encoding,
 | ||
|  | // and after decoding accordingly.
 | ||
|  | 
 | ||
|  | static uint32_t smolv_EncodeLen(SpvOp op, uint32_t len) | ||
|  | { | ||
|  | 	len--; | ||
|  | 	if (op == SpvOpVectorShuffle)			len -= 4; | ||
|  | 	if (op == SpvOpVectorShuffleCompact)	len -= 4; | ||
|  | 	if (op == SpvOpDecorate)				len -= 2; | ||
|  | 	if (op == SpvOpLoad)					len -= 3; | ||
|  | 	if (op == SpvOpAccessChain)				len -= 3; | ||
|  | 	return len; | ||
|  | } | ||
|  | 
 | ||
|  | static uint32_t smolv_DecodeLen(SpvOp op, uint32_t len) | ||
|  | { | ||
|  | 	len++; | ||
|  | 	if (op == SpvOpVectorShuffle)			len += 4; | ||
|  | 	if (op == SpvOpVectorShuffleCompact)	len += 4; | ||
|  | 	if (op == SpvOpDecorate)				len += 2; | ||
|  | 	if (op == SpvOpLoad)					len += 3; | ||
|  | 	if (op == SpvOpAccessChain)				len += 3; | ||
|  | 	return len; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // Shuffling bits of length + opcode to be more compact in varint encoding in typical cases:
 | ||
|  | // 0x LLLL OOOO is how SPIR-V encodes it (L=length, O=op), we shuffle into:
 | ||
|  | // 0x LLLO OOLO, so that common case (op<16, len<8) is encoded into one byte.
 | ||
|  | 
 | ||
|  | static bool smolv_WriteLengthOp(smolv::ByteArray& arr, uint32_t len, SpvOp op) | ||
|  | { | ||
|  | 	len = smolv_EncodeLen(op, len); | ||
|  | 	// SPIR-V length field is 16 bits; if we get a larger value that means something
 | ||
|  | 	// was wrong, e.g. a vector shuffle instruction with less than 4 words (and our
 | ||
|  | 	// adjustment to common lengths in smolv_EncodeLen wrapped around)
 | ||
|  | 	if (len > 0xFFFF) | ||
|  | 		return false; | ||
|  | 	op = smolv_RemapOp(op); | ||
|  | 	uint32_t oplen = ((len >> 4) << 20) | ((op >> 4) << 8) | ((len & 0xF) << 4) | (op & 0xF); | ||
|  | 	smolv_WriteVarint(arr, oplen); | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | static bool smolv_ReadLengthOp(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outLen, SpvOp& outOp) | ||
|  | { | ||
|  | 	uint32_t val; | ||
|  | 	if (!smolv_ReadVarint(data, dataEnd, val)) | ||
|  | 		return false; | ||
|  | 	outLen = ((val >> 20) << 4) | ((val >> 4) & 0xF); | ||
|  | 	outOp = (SpvOp)(((val >> 4) & 0xFFF0) | (val & 0xF)); | ||
|  | 
 | ||
|  | 	outOp = smolv_RemapOp(outOp); | ||
|  | 	outLen = smolv_DecodeLen(outOp, outLen); | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #define _SMOLV_READ_OP(len, words, op) \
 | ||
|  | 	uint32_t len = words[0] >> 16; \ | ||
|  | 	if (len < 1) return false; /* malformed instruction, length needs to be at least 1 */ \ | ||
|  | 	if (words + len > wordsEnd) return false; /* malformed instruction, goes past end of data */ \ | ||
|  | 	SpvOp op = (SpvOp)(words[0] & 0xFFFF) | ||
|  | 
 | ||
|  | 
 | ||
|  | bool smolv::Encode(const void* spirvData, size_t spirvSize, ByteArray& outSmolv, uint32_t flags, StripOpNameFilterFunc stripFilter) | ||
|  | { | ||
|  | 	const size_t wordCount = spirvSize / 4; | ||
|  | 	if (wordCount * 4 != spirvSize) | ||
|  | 		return false; | ||
|  | 	const uint32_t* words = (const uint32_t*)spirvData; | ||
|  | 	const uint32_t* wordsEnd = words + wordCount; | ||
|  | 	if (!smolv_CheckSpirVHeader(words, wordCount)) | ||
|  | 		return false; | ||
|  | 
 | ||
|  | 	// reserve space in output (typical compression is to about 30%; reserve half of input space)
 | ||
|  | 	outSmolv.reserve(outSmolv.size() + spirvSize/2); | ||
|  | 
 | ||
|  | 	// header (matches SPIR-V one, except different magic)
 | ||
|  | 	smolv_Write4(outSmolv, kSmolHeaderMagic); | ||
|  | 	smolv_Write4(outSmolv, (words[1] & 0x00FFFFFF) + (kSmolCurrEncodingVersion<<24)); // SPIR-V version (_XXX) + SMOL-V version (X___)
 | ||
|  | 	smolv_Write4(outSmolv, words[2]); // generator
 | ||
|  | 	smolv_Write4(outSmolv, words[3]); // bound
 | ||
|  | 	smolv_Write4(outSmolv, words[4]); // schema
 | ||
|  | 
 | ||
|  | 	const size_t headerSpirvSizeOffset = outSmolv.size(); // size field may get updated later if stripping is enabled
 | ||
|  | 	smolv_Write4(outSmolv, (uint32_t)spirvSize); // space needed to decode (i.e. original SPIR-V size)
 | ||
|  | 
 | ||
|  | 	size_t strippedSpirvWordCount = wordCount; | ||
|  | 	uint32_t prevResult = 0; | ||
|  | 	uint32_t prevDecorate = 0; | ||
|  | 	 | ||
|  | 	const int knownOpsCount = smolv_GetKnownOpsCount(kSmolCurrEncodingVersion); | ||
|  | 
 | ||
|  | 	words += 5; | ||
|  | 	while (words < wordsEnd) | ||
|  | 	{ | ||
|  | 		_SMOLV_READ_OP(instrLen, words, op); | ||
|  | 
 | ||
|  | 		if ((flags & kEncodeFlagStripDebugInfo) && smolv_OpDebugInfo(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			if (!stripFilter || op != SpvOpName || !stripFilter(reinterpret_cast<const char*>(&words[2]))) | ||
|  | 			{ | ||
|  | 				strippedSpirvWordCount -= instrLen; | ||
|  | 				words += instrLen; | ||
|  | 				continue; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// A usual case of vector shuffle, with less than 4 components, each with a value
 | ||
|  | 		// in [0..3] range: encode it in a more compact form, with the swizzle pattern in one byte.
 | ||
|  | 		// Turn this into a VectorShuffleCompact instruction, that takes up unused slot in Ops.
 | ||
|  | 		uint32_t swizzle = 0; | ||
|  | 		if (op == SpvOpVectorShuffle && instrLen <= 9) | ||
|  | 		{ | ||
|  | 			uint32_t swz0 = instrLen > 5 ? words[5] : 0; | ||
|  | 			uint32_t swz1 = instrLen > 6 ? words[6] : 0; | ||
|  | 			uint32_t swz2 = instrLen > 7 ? words[7] : 0; | ||
|  | 			uint32_t swz3 = instrLen > 8 ? words[8] : 0; | ||
|  | 			if (swz0 < 4 && swz1 < 4 && swz2 < 4 && swz3 < 4) | ||
|  | 			{ | ||
|  | 				op = SpvOpVectorShuffleCompact; | ||
|  | 				swizzle = (swz0 << 6) | (swz1 << 4) | (swz2 << 2) | (swz3); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// length + opcode
 | ||
|  | 		if (!smolv_WriteLengthOp(outSmolv, instrLen, op)) | ||
|  | 			return false; | ||
|  | 
 | ||
|  | 		size_t ioffs = 1; | ||
|  | 		// write type as varint, if we have it
 | ||
|  | 		if (smolv_OpHasType(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			if (ioffs >= instrLen) | ||
|  | 				return false; | ||
|  | 			smolv_WriteVarint(outSmolv, words[ioffs]); | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		// write result as delta+zig+varint, if we have it
 | ||
|  | 		if (smolv_OpHasResult(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			if (ioffs >= instrLen) | ||
|  | 				return false; | ||
|  | 			uint32_t v = words[ioffs]; | ||
|  | 			smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevResult)); // some deltas are negative, use zig
 | ||
|  | 			prevResult = v; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Decorate & MemberDecorate: IDs relative to previous decorate
 | ||
|  | 		if (op == SpvOpDecorate || op == SpvOpMemberDecorate) | ||
|  | 		{ | ||
|  | 			if (ioffs >= instrLen) | ||
|  | 				return false; | ||
|  | 			uint32_t v = words[ioffs]; | ||
|  | 			smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevDecorate)); // spirv-remapped deltas often negative, use zig
 | ||
|  | 			prevDecorate = v; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// MemberDecorate special encoding: whole row of MemberDecorate instructions is often referring
 | ||
|  | 		// to the same type and linearly increasing member indices. Scan ahead to see how many we have,
 | ||
|  | 		// and encode whole bunch as one.
 | ||
|  | 		if (op == SpvOpMemberDecorate) | ||
|  | 		{ | ||
|  | 			// scan ahead until we reach end, non-member-decoration or different type
 | ||
|  | 			const uint32_t decorationType = words[ioffs-1]; | ||
|  | 			const uint32_t* memberWords = words; | ||
|  | 			uint32_t prevIndex = 0; | ||
|  | 			uint32_t prevOffset = 0; | ||
|  | 			// write a byte on how many we have encoded as a bunch
 | ||
|  | 			size_t countLocation = outSmolv.size(); | ||
|  | 			outSmolv.push_back(0); | ||
|  | 			int count = 0; | ||
|  | 			while (memberWords < wordsEnd && count < 255) | ||
|  | 			{ | ||
|  | 				_SMOLV_READ_OP(memberLen, memberWords, memberOp); | ||
|  | 				if (memberOp != SpvOpMemberDecorate) | ||
|  | 					break; | ||
|  | 				if (memberLen < 4) | ||
|  | 					return false; // invalid input
 | ||
|  | 				if (memberWords[1] != decorationType) | ||
|  | 					break; | ||
|  | 
 | ||
|  | 				// write member index as delta from previous
 | ||
|  | 				uint32_t memberIndex = memberWords[2]; | ||
|  | 				smolv_WriteVarint(outSmolv, memberIndex - prevIndex); | ||
|  | 				prevIndex = memberIndex; | ||
|  | 
 | ||
|  | 				// decoration (and length if not common/known)
 | ||
|  | 				uint32_t memberDec = memberWords[3]; | ||
|  | 				smolv_WriteVarint(outSmolv, memberDec); | ||
|  | 				const int knownExtraOps = smolv_DecorationExtraOps(memberDec); | ||
|  | 				if (knownExtraOps == -1) | ||
|  | 					smolv_WriteVarint(outSmolv, memberLen-4); | ||
|  | 				else if (unsigned(knownExtraOps) + 4 != memberLen) | ||
|  | 					return false; // invalid input
 | ||
|  | 
 | ||
|  | 				// Offset decorations are most often linearly increasing, so encode as deltas
 | ||
|  | 				if (memberDec == 35) // Offset
 | ||
|  | 				{ | ||
|  | 					if (memberLen != 5) | ||
|  | 						return false; | ||
|  | 					smolv_WriteVarint(outSmolv, memberWords[4]-prevOffset); | ||
|  | 					prevOffset = memberWords[4]; | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					// write rest of decorations as varint
 | ||
|  | 					for (uint32_t i = 4; i < memberLen; ++i) | ||
|  | 						smolv_WriteVarint(outSmolv, memberWords[i]); | ||
|  | 				} | ||
|  | 
 | ||
|  | 				memberWords += memberLen; | ||
|  | 				++count; | ||
|  | 			} | ||
|  | 			outSmolv[countLocation] = uint8_t(count); | ||
|  | 			words = memberWords; | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Write out this many IDs, encoding them relative+zigzag to result ID
 | ||
|  | 		int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount); | ||
|  | 		for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs) | ||
|  | 		{ | ||
|  | 			if (ioffs >= instrLen) | ||
|  | 				return false; | ||
|  | 			uint32_t delta = prevResult - words[ioffs]; | ||
|  | 			// some deltas are negative (often on branches, or if program was processed by spirv-remap),
 | ||
|  | 			// so use zig encoding
 | ||
|  | 			smolv_WriteVarint(outSmolv, smolv_ZigEncode(delta)); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (op == SpvOpVectorShuffleCompact) | ||
|  | 		{ | ||
|  | 			// compact vector shuffle, just write out single swizzle byte
 | ||
|  | 			outSmolv.push_back(uint8_t(swizzle)); | ||
|  | 			ioffs = instrLen; | ||
|  | 		} | ||
|  | 		else if (smolv_OpVarRest(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			// write out rest of words with variable encoding (expected to be small integers)
 | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 				smolv_WriteVarint(outSmolv, words[ioffs]); | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			// write out rest of words without any encoding
 | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 				smolv_Write4(outSmolv, words[ioffs]); | ||
|  | 		} | ||
|  | 		 | ||
|  | 		words += instrLen; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (strippedSpirvWordCount != wordCount) | ||
|  | 	{ | ||
|  | 		uint8_t* headerSpirvSize = &outSmolv[headerSpirvSizeOffset]; | ||
|  | 		smolv_Write4(headerSpirvSize, (uint32_t)strippedSpirvWordCount * 4); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | size_t smolv::GetDecodedBufferSize(const void* smolvData, size_t smolvSize) | ||
|  | { | ||
|  | 	if (!smolv_CheckSmolHeader((const uint8_t*)smolvData, smolvSize)) | ||
|  | 		return 0; | ||
|  | 	const uint32_t* words = (const uint32_t*)smolvData; | ||
|  | 	return words[5]; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | bool smolv::Decode(const void* smolvData, size_t smolvSize, void* spirvOutputBuffer, size_t spirvOutputBufferSize, uint32_t flags) | ||
|  | { | ||
|  | 	// check header, and whether we have enough output buffer space
 | ||
|  | 	const size_t neededBufferSize = GetDecodedBufferSize(smolvData, smolvSize); | ||
|  | 	if (neededBufferSize == 0) | ||
|  | 		return false; // invalid SMOL-V
 | ||
|  | 	if (spirvOutputBufferSize < neededBufferSize) | ||
|  | 		return false; // not enough space in output buffer
 | ||
|  | 	if (spirvOutputBuffer == NULL) | ||
|  | 		return false; // output buffer is null
 | ||
|  | 
 | ||
|  | 	const uint8_t* bytes = (const uint8_t*)smolvData; | ||
|  | 	const uint8_t* bytesEnd = bytes + smolvSize; | ||
|  | 
 | ||
|  | 	uint8_t* outSpirv = (uint8_t*)spirvOutputBuffer; | ||
|  | 	 | ||
|  | 	uint32_t val; | ||
|  | 	int smolVersion = 0; | ||
|  | 
 | ||
|  | 	// header
 | ||
|  | 	smolv_Write4(outSpirv, kSpirVHeaderMagic); bytes += 4; | ||
|  | 	smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24; val &= 0x00FFFFFF; smolv_Write4(outSpirv, val); // version
 | ||
|  | 	smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // generator
 | ||
|  | 	smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // bound
 | ||
|  | 	smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // schema
 | ||
|  | 	bytes += 4; // decode buffer size
 | ||
|  | 	 | ||
|  | 	// there are two SMOL-V encoding versions, both not indicating anything in their header version field:
 | ||
|  | 	// one that is called "before zero" here (2016-08-31 code). Support decoding that one only by presence
 | ||
|  | 	// of this special flag.
 | ||
|  | 	const bool beforeZeroVersion = smolVersion == 0 && (flags & kDecodeFlagUse20160831AsZeroVersion) != 0; | ||
|  | 
 | ||
|  | 	const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion); | ||
|  | 
 | ||
|  | 	uint32_t prevResult = 0; | ||
|  | 	uint32_t prevDecorate = 0; | ||
|  | 
 | ||
|  | 	while (bytes < bytesEnd) | ||
|  | 	{ | ||
|  | 		// read length + opcode
 | ||
|  | 		uint32_t instrLen; | ||
|  | 		SpvOp op; | ||
|  | 		if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op)) | ||
|  | 			return false; | ||
|  | 		const bool wasSwizzle = (op == SpvOpVectorShuffleCompact); | ||
|  | 		if (wasSwizzle) | ||
|  | 			op = SpvOpVectorShuffle; | ||
|  | 		smolv_Write4(outSpirv, (instrLen << 16) | op); | ||
|  | 
 | ||
|  | 		size_t ioffs = 1; | ||
|  | 
 | ||
|  | 		// read type as varint, if we have it
 | ||
|  | 		if (smolv_OpHasType(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			smolv_Write4(outSpirv, val); | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		// read result as delta+varint, if we have it
 | ||
|  | 		if (smolv_OpHasResult(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			val = prevResult + smolv_ZigDecode(val); | ||
|  | 			smolv_Write4(outSpirv, val); | ||
|  | 			prevResult = val; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		 | ||
|  | 		// Decorate: IDs relative to previous decorate
 | ||
|  | 		if (op == SpvOpDecorate || op == SpvOpMemberDecorate) | ||
|  | 		{ | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			// "before zero" version did not use zig encoding for the value
 | ||
|  | 			val = prevDecorate + (beforeZeroVersion ? val : smolv_ZigDecode(val)); | ||
|  | 			smolv_Write4(outSpirv, val); | ||
|  | 			prevDecorate = val; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// MemberDecorate special decoding
 | ||
|  | 		if (op == SpvOpMemberDecorate && !beforeZeroVersion) | ||
|  | 		{ | ||
|  | 			if (bytes >= bytesEnd) | ||
|  | 				return false; // broken input
 | ||
|  | 			int count = *bytes++; | ||
|  | 			int prevIndex = 0; | ||
|  | 			int prevOffset = 0; | ||
|  | 			for (int m = 0; m < count; ++m) | ||
|  | 			{ | ||
|  | 				// read member index
 | ||
|  | 				uint32_t memberIndex; | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false; | ||
|  | 				memberIndex += prevIndex; | ||
|  | 				prevIndex = memberIndex; | ||
|  | 				 | ||
|  | 				// decoration (and length if not common/known)
 | ||
|  | 				uint32_t memberDec; | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false; | ||
|  | 				const int knownExtraOps = smolv_DecorationExtraOps(memberDec); | ||
|  | 				uint32_t memberLen; | ||
|  | 				if (knownExtraOps == -1) | ||
|  | 				{ | ||
|  | 					if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false; | ||
|  | 					memberLen += 4; | ||
|  | 				} | ||
|  | 				else | ||
|  | 					memberLen = 4 + knownExtraOps; | ||
|  | 
 | ||
|  | 				// write SPIR-V op+length (unless it's first member decoration, in which case it was written before)
 | ||
|  | 				if (m != 0) | ||
|  | 				{ | ||
|  | 					smolv_Write4(outSpirv, (memberLen << 16) | op); | ||
|  | 					smolv_Write4(outSpirv, prevDecorate); | ||
|  | 				} | ||
|  | 				smolv_Write4(outSpirv, memberIndex); | ||
|  | 				smolv_Write4(outSpirv, memberDec); | ||
|  | 				// Special case for Offset decorations
 | ||
|  | 				if (memberDec == 35) // Offset
 | ||
|  | 				{ | ||
|  | 					if (memberLen != 5) | ||
|  | 						return false; | ||
|  | 					if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 					val += prevOffset; | ||
|  | 					smolv_Write4(outSpirv, val); | ||
|  | 					prevOffset = val; | ||
|  | 				} | ||
|  | 				else | ||
|  | 				{ | ||
|  | 					for (uint32_t i = 4; i < memberLen; ++i) | ||
|  | 					{ | ||
|  | 						if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 						smolv_Write4(outSpirv, val); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Read this many IDs, that are relative to result ID
 | ||
|  | 		int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount); | ||
|  | 		// "before zero" version only used zig encoding for IDs of several ops; after
 | ||
|  | 		// that ops got zig encoding for their IDs
 | ||
|  | 		bool zigDecodeVals = true; | ||
|  | 		if (beforeZeroVersion) | ||
|  | 		{ | ||
|  | 			if (op != SpvOpControlBarrier && op != SpvOpMemoryBarrier && op != SpvOpLoopMerge && op != SpvOpSelectionMerge && op != SpvOpBranch && op != SpvOpBranchConditional && op != SpvOpMemoryNamedBarrier) | ||
|  | 				zigDecodeVals = false; | ||
|  | 		} | ||
|  | 		for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs) | ||
|  | 		{ | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			if (zigDecodeVals) | ||
|  | 				val = smolv_ZigDecode(val); | ||
|  | 			smolv_Write4(outSpirv, prevResult - val); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (wasSwizzle && instrLen <= 9) | ||
|  | 		{ | ||
|  | 			uint32_t swizzle = *bytes++; | ||
|  | 			if (instrLen > 5) smolv_Write4(outSpirv, (swizzle >> 6) & 3); | ||
|  | 			if (instrLen > 6) smolv_Write4(outSpirv, (swizzle >> 4) & 3); | ||
|  | 			if (instrLen > 7) smolv_Write4(outSpirv, (swizzle >> 2) & 3); | ||
|  | 			if (instrLen > 8) smolv_Write4(outSpirv, swizzle & 3); | ||
|  | 		} | ||
|  | 		else if (smolv_OpVarRest(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			// read rest of words with variable encoding
 | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 			{ | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 				smolv_Write4(outSpirv, val); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			// read rest of words without any encoding
 | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 			{ | ||
|  | 				if (!smolv_Read4(bytes, bytesEnd, val)) return false; | ||
|  | 				smolv_Write4(outSpirv, val); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ((uint8_t*)spirvOutputBuffer + neededBufferSize != outSpirv) | ||
|  | 		return false; // something went wrong during decoding? we should have decoded to exact output size
 | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | // --------------------------------------------------------------------------------------------
 | ||
|  | // Calculating instruction count / space stats on SPIR-V and SMOL-V
 | ||
|  | 
 | ||
|  | 
 | ||
|  | struct smolv::Stats | ||
|  | { | ||
|  | 	Stats() { memset(this, 0, sizeof(*this)); } | ||
|  | 	size_t opCounts[kKnownOpsCount]; | ||
|  | 	size_t opSizes[kKnownOpsCount]; | ||
|  | 	size_t smolOpSizes[kKnownOpsCount]; | ||
|  | 	size_t varintCountsOp[6]; | ||
|  | 	size_t varintCountsType[6]; | ||
|  | 	size_t varintCountsRes[6]; | ||
|  | 	size_t varintCountsOther[6]; | ||
|  | 	size_t totalOps; | ||
|  | 	size_t totalSize; | ||
|  | 	size_t totalSizeSmol; | ||
|  | 	size_t inputCount; | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | smolv::Stats* smolv::StatsCreate() | ||
|  | { | ||
|  | 	return new Stats(); | ||
|  | } | ||
|  | 
 | ||
|  | void smolv::StatsDelete(smolv::Stats *s) | ||
|  | { | ||
|  | 	delete s; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | bool smolv::StatsCalculate(smolv::Stats* stats, const void* spirvData, size_t spirvSize) | ||
|  | { | ||
|  | 	if (!stats) | ||
|  | 		return false; | ||
|  | 
 | ||
|  | 	const size_t wordCount = spirvSize / 4; | ||
|  | 	if (wordCount * 4 != spirvSize) | ||
|  | 		return false; | ||
|  | 	const uint32_t* words = (const uint32_t*)spirvData; | ||
|  | 	const uint32_t* wordsEnd = words + wordCount; | ||
|  | 	if (!smolv_CheckSpirVHeader(words, wordCount)) | ||
|  | 		return false; | ||
|  | 	words += 5; | ||
|  | 	 | ||
|  | 	stats->inputCount++; | ||
|  | 	stats->totalSize += wordCount; | ||
|  | 
 | ||
|  | 	while (words < wordsEnd) | ||
|  | 	{ | ||
|  | 		_SMOLV_READ_OP(instrLen, words, op); | ||
|  | 
 | ||
|  | 		if (op < kKnownOpsCount) | ||
|  | 		{ | ||
|  | 			stats->opCounts[op]++; | ||
|  | 			stats->opSizes[op] += instrLen; | ||
|  | 		} | ||
|  | 		words += instrLen; | ||
|  | 		stats->totalOps++; | ||
|  | 	} | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | bool smolv::StatsCalculateSmol(smolv::Stats* stats, const void* smolvData, size_t smolvSize) | ||
|  | { | ||
|  | 	if (!stats) | ||
|  | 		return false; | ||
|  | 
 | ||
|  | 	// debugging helper to dump all encoded bytes to stdout, keep at "if 0"
 | ||
|  | #	if 0
 | ||
|  | #		define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() { \
 | ||
|  | 			printf("Op %-22s ", op < kKnownOpsCount ? kSpirvOpNames[op] : "???"); \ | ||
|  | 			for (const uint8_t* b = instrBegin; b < bytes; ++b) \ | ||
|  | 				printf("%02x ", *b); \ | ||
|  | 			printf("\n"); \ | ||
|  | 		} | ||
|  | #	else
 | ||
|  | #		define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() {}
 | ||
|  | #	endif
 | ||
|  | 	 | ||
|  | 	const uint8_t* bytes = (const uint8_t*)smolvData; | ||
|  | 	const uint8_t* bytesEnd = bytes + smolvSize; | ||
|  | 	if (!smolv_CheckSmolHeader(bytes, smolvSize)) | ||
|  | 		return false; | ||
|  | 
 | ||
|  | 	uint32_t val; | ||
|  | 	int smolVersion; | ||
|  | 	bytes += 4; | ||
|  | 	smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24; | ||
|  | 	const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion); | ||
|  | 	bytes += 16; | ||
|  | 	 | ||
|  | 	stats->totalSizeSmol += smolvSize; | ||
|  | 	 | ||
|  | 	while (bytes < bytesEnd) | ||
|  | 	{ | ||
|  | 		const uint8_t* instrBegin = bytes; | ||
|  | 		const uint8_t* varBegin; | ||
|  | 
 | ||
|  | 		// read length + opcode
 | ||
|  | 		uint32_t instrLen; | ||
|  | 		SpvOp op; | ||
|  | 		varBegin = bytes; | ||
|  | 		if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op)) | ||
|  | 			return false; | ||
|  | 		const bool wasSwizzle = (op == SpvOpVectorShuffleCompact); | ||
|  | 		if (wasSwizzle) | ||
|  | 			op = SpvOpVectorShuffle; | ||
|  | 		stats->varintCountsOp[bytes-varBegin]++; | ||
|  | 		 | ||
|  | 		size_t ioffs = 1; | ||
|  | 		if (smolv_OpHasType(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			varBegin = bytes; | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			stats->varintCountsType[bytes-varBegin]++; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		if (smolv_OpHasResult(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			varBegin = bytes; | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			stats->varintCountsRes[bytes-varBegin]++; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		 | ||
|  | 		if (op == SpvOpDecorate || op == SpvOpMemberDecorate) | ||
|  | 		{ | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			ioffs++; | ||
|  | 		} | ||
|  | 		// MemberDecorate special decoding
 | ||
|  | 		if (op == SpvOpMemberDecorate) | ||
|  | 		{ | ||
|  | 			if (bytes >= bytesEnd) | ||
|  | 				return false; // broken input
 | ||
|  | 			int count = *bytes++; | ||
|  | 			for (int m = 0; m < count; ++m) | ||
|  | 			{ | ||
|  | 				uint32_t memberIndex; | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false; | ||
|  | 				uint32_t memberDec; | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false; | ||
|  | 				const int knownExtraOps = smolv_DecorationExtraOps(memberDec); | ||
|  | 				uint32_t memberLen; | ||
|  | 				if (knownExtraOps == -1) | ||
|  | 				{ | ||
|  | 					if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false; | ||
|  | 					memberLen += 4; | ||
|  | 				} | ||
|  | 				else | ||
|  | 					memberLen = 4 + knownExtraOps; | ||
|  | 				for (uint32_t i = 4; i < memberLen; ++i) | ||
|  | 				{ | ||
|  | 					if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			stats->smolOpSizes[op] += bytes - instrBegin; | ||
|  | 			_SMOLV_DEBUG_PRINT_ENCODED_BYTES(); | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount); | ||
|  | 		for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs) | ||
|  | 		{ | ||
|  | 			varBegin = bytes; | ||
|  | 			if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 			stats->varintCountsRes[bytes-varBegin]++; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (wasSwizzle && instrLen <= 9) | ||
|  | 		{ | ||
|  | 			bytes++; | ||
|  | 		} | ||
|  | 		else if (smolv_OpVarRest(op, knownOpsCount)) | ||
|  | 		{ | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 			{ | ||
|  | 				varBegin = bytes; | ||
|  | 				if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false; | ||
|  | 				stats->varintCountsOther[bytes-varBegin]++; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			for (; ioffs < instrLen; ++ioffs) | ||
|  | 			{ | ||
|  | 				if (!smolv_Read4(bytes, bytesEnd, val)) return false; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		 | ||
|  | 		if (op < kKnownOpsCount) | ||
|  | 		{ | ||
|  | 			stats->smolOpSizes[op] += bytes - instrBegin; | ||
|  | 		} | ||
|  | 		_SMOLV_DEBUG_PRINT_ENCODED_BYTES(); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | static bool CompareOpCounters (std::pair<SpvOp,size_t> a, std::pair<SpvOp,size_t> b) | ||
|  | { | ||
|  | 	return a.second > b.second; | ||
|  | } | ||
|  | 
 | ||
|  | void smolv::StatsPrint(const Stats* stats) | ||
|  | { | ||
|  | 	if (!stats) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	typedef std::pair<SpvOp,size_t> OpCounter; | ||
|  | 	OpCounter counts[kKnownOpsCount]; | ||
|  | 	OpCounter sizes[kKnownOpsCount]; | ||
|  | 	OpCounter sizesSmol[kKnownOpsCount]; | ||
|  | 	for (int i = 0; i < kKnownOpsCount; ++i) | ||
|  | 	{ | ||
|  | 		counts[i].first = (SpvOp)i; | ||
|  | 		counts[i].second = stats->opCounts[i]; | ||
|  | 		sizes[i].first = (SpvOp)i; | ||
|  | 		sizes[i].second = stats->opSizes[i]; | ||
|  | 		sizesSmol[i].first = (SpvOp)i; | ||
|  | 		sizesSmol[i].second = stats->smolOpSizes[i]; | ||
|  | 	} | ||
|  | 	std::sort(counts, counts + kKnownOpsCount, CompareOpCounters); | ||
|  | 	std::sort(sizes, sizes + kKnownOpsCount, CompareOpCounters); | ||
|  | 	std::sort(sizesSmol, sizesSmol + kKnownOpsCount, CompareOpCounters); | ||
|  | 	 | ||
|  | 	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); | ||
|  | 	printf("Most occuring ops:\n"); | ||
|  | 	for (int i = 0; i < 30; ++i) | ||
|  | 	{ | ||
|  | 		SpvOp op = counts[i].first; | ||
|  | 		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); | ||
|  | 	} | ||
|  | 	printf("Largest total size of ops:\n"); | ||
|  | 	for (int i = 0; i < 30; ++i) | ||
|  | 	{ | ||
|  | 		SpvOp op = sizes[i].first; | ||
|  | 		printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n", | ||
|  | 			   i, | ||
|  | 			   kSpirvOpNames[op], | ||
|  | 			   (int)sizes[i].second*4, | ||
|  | 			   (float)sizes[i].second / (float)stats->totalSize * 100.0f, | ||
|  | 			   (float)sizes[i].second*4 / (float)stats->opCounts[op] | ||
|  | 		); | ||
|  | 	} | ||
|  | 	printf("SMOL varint encoding counts per byte length:\n"); | ||
|  | 	printf("  B: %6s %6s %6s %6s\n", "Op", "Type", "Result", "Other"); | ||
|  | 	for (int i = 1; i < 6; ++i) | ||
|  | 	{ | ||
|  | 		printf("  %i: %6i %6i %6i %6i\n", i, (int)stats->varintCountsOp[i], (int)stats->varintCountsType[i], (int)stats->varintCountsRes[i], (int)stats->varintCountsOther[i]); | ||
|  | 	} | ||
|  | 	printf("Largest total size of ops in SMOL:\n"); | ||
|  | 	for (int i = 0; i < 30; ++i) | ||
|  | 	{ | ||
|  | 		SpvOp op = sizesSmol[i].first; | ||
|  | 		printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n", | ||
|  | 			   i, | ||
|  | 			   kSpirvOpNames[op], | ||
|  | 			   (int)sizesSmol[i].second, | ||
|  | 			   (float)sizesSmol[i].second / (float)stats->totalSizeSmol * 100.0f, | ||
|  | 			   (float)sizesSmol[i].second / (float)stats->opCounts[op] | ||
|  | 		); | ||
|  | 	}	 | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | // ------------------------------------------------------------------------------
 | ||
|  | // This software is available under 2 licenses -- choose whichever you prefer.
 | ||
|  | // ------------------------------------------------------------------------------
 | ||
|  | // ALTERNATIVE A - MIT License
 | ||
|  | // Copyright (c) 2016-2020 Aras Pranckevicius
 | ||
|  | // Permission is hereby granted, free of charge, to any person obtaining a copy of
 | ||
|  | // this software and associated documentation files (the "Software"), to deal in
 | ||
|  | // the Software without restriction, including without limitation the rights to
 | ||
|  | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 | ||
|  | // of the Software, and to permit persons to whom the Software is furnished to do
 | ||
|  | // so, subject to the following conditions:
 | ||
|  | // The above copyright notice and this permission notice shall be included in all
 | ||
|  | // copies or substantial portions of the Software.
 | ||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||
|  | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||
|  | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||
|  | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||
|  | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | ||
|  | // SOFTWARE.
 | ||
|  | // ------------------------------------------------------------------------------
 | ||
|  | // ALTERNATIVE B - Public Domain (www.unlicense.org)
 | ||
|  | // This is free and unencumbered software released into the public domain.
 | ||
|  | // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
 | ||
|  | // software, either in source code form or as a compiled binary, for any purpose,
 | ||
|  | // commercial or non-commercial, and by any means.
 | ||
|  | // In jurisdictions that recognize copyright laws, the author or authors of this
 | ||
|  | // software dedicate any and all copyright interest in the software to the public
 | ||
|  | // domain. We make this dedication for the benefit of the public at large and to
 | ||
|  | // the detriment of our heirs and successors. We intend this dedication to be an
 | ||
|  | // overt act of relinquishment in perpetuity of all present and future rights to
 | ||
|  | // this software under copyright law.
 | ||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||
|  | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||
|  | // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | ||
|  | // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | ||
|  | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | ||
|  | // ------------------------------------------------------------------------------
 |