diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,7 @@
+# Changelog for spirv-reflect-ffi
+
+## [0.1] - 2022-09-04
+
+* Initial release.
+
+[0.1]: https://gitlab.com/dpwiz/spirv-reflect/-/tree/v0.1-ffi
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright IC Rainbow (c) 2022
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Author name here nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cbits/include/spirv/unified1/spirv.h b/cbits/include/spirv/unified1/spirv.h
new file mode 100644
--- /dev/null
+++ b/cbits/include/spirv/unified1/spirv.h
@@ -0,0 +1,2542 @@
+/*
+** Copyright (c) 2014-2020 The Khronos Group Inc.
+** 
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are 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 Materials.
+** 
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 
+** 
+** THE MATERIALS ARE 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 MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+/*
+** This header is automatically generated by the same tool that creates
+** the Binary Section of the SPIR-V specification.
+*/
+
+/*
+** Enumeration tokens for SPIR-V, in various styles:
+**   C, C++, C++11, JSON, Lua, Python, C#, D
+** 
+** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+** - C# will use enum classes in the Specification class located in the "Spv" namespace,
+**     e.g.: Spv.Specification.SourceLanguage.GLSL
+** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
+** 
+** Some tokens act like mask values, which can be OR'd together,
+** while others are mutually exclusive.  The mask-like ones have
+** "Mask" in their name, and a parallel enum that has the shift
+** amount (1 << x) for each corresponding enumerant.
+*/
+
+#ifndef spirv_H
+#define spirv_H
+
+typedef unsigned int SpvId;
+
+#define SPV_VERSION 0x10600
+#define SPV_REVISION 1
+
+static const unsigned int SpvMagicNumber = 0x07230203;
+static const unsigned int SpvVersion = 0x00010600;
+static const unsigned int SpvRevision = 1;
+static const unsigned int SpvOpCodeMask = 0xffff;
+static const unsigned int SpvWordCountShift = 16;
+
+typedef enum SpvSourceLanguage_ {
+    SpvSourceLanguageUnknown = 0,
+    SpvSourceLanguageESSL = 1,
+    SpvSourceLanguageGLSL = 2,
+    SpvSourceLanguageOpenCL_C = 3,
+    SpvSourceLanguageOpenCL_CPP = 4,
+    SpvSourceLanguageHLSL = 5,
+    SpvSourceLanguageCPP_for_OpenCL = 6,
+    SpvSourceLanguageSYCL = 7,
+    SpvSourceLanguageMax = 0x7fffffff,
+} SpvSourceLanguage;
+
+typedef enum SpvExecutionModel_ {
+    SpvExecutionModelVertex = 0,
+    SpvExecutionModelTessellationControl = 1,
+    SpvExecutionModelTessellationEvaluation = 2,
+    SpvExecutionModelGeometry = 3,
+    SpvExecutionModelFragment = 4,
+    SpvExecutionModelGLCompute = 5,
+    SpvExecutionModelKernel = 6,
+    SpvExecutionModelTaskNV = 5267,
+    SpvExecutionModelMeshNV = 5268,
+    SpvExecutionModelRayGenerationKHR = 5313,
+    SpvExecutionModelRayGenerationNV = 5313,
+    SpvExecutionModelIntersectionKHR = 5314,
+    SpvExecutionModelIntersectionNV = 5314,
+    SpvExecutionModelAnyHitKHR = 5315,
+    SpvExecutionModelAnyHitNV = 5315,
+    SpvExecutionModelClosestHitKHR = 5316,
+    SpvExecutionModelClosestHitNV = 5316,
+    SpvExecutionModelMissKHR = 5317,
+    SpvExecutionModelMissNV = 5317,
+    SpvExecutionModelCallableKHR = 5318,
+    SpvExecutionModelCallableNV = 5318,
+    SpvExecutionModelMax = 0x7fffffff,
+} SpvExecutionModel;
+
+typedef enum SpvAddressingModel_ {
+    SpvAddressingModelLogical = 0,
+    SpvAddressingModelPhysical32 = 1,
+    SpvAddressingModelPhysical64 = 2,
+    SpvAddressingModelPhysicalStorageBuffer64 = 5348,
+    SpvAddressingModelPhysicalStorageBuffer64EXT = 5348,
+    SpvAddressingModelMax = 0x7fffffff,
+} SpvAddressingModel;
+
+typedef enum SpvMemoryModel_ {
+    SpvMemoryModelSimple = 0,
+    SpvMemoryModelGLSL450 = 1,
+    SpvMemoryModelOpenCL = 2,
+    SpvMemoryModelVulkan = 3,
+    SpvMemoryModelVulkanKHR = 3,
+    SpvMemoryModelMax = 0x7fffffff,
+} SpvMemoryModel;
+
+typedef enum SpvExecutionMode_ {
+    SpvExecutionModeInvocations = 0,
+    SpvExecutionModeSpacingEqual = 1,
+    SpvExecutionModeSpacingFractionalEven = 2,
+    SpvExecutionModeSpacingFractionalOdd = 3,
+    SpvExecutionModeVertexOrderCw = 4,
+    SpvExecutionModeVertexOrderCcw = 5,
+    SpvExecutionModePixelCenterInteger = 6,
+    SpvExecutionModeOriginUpperLeft = 7,
+    SpvExecutionModeOriginLowerLeft = 8,
+    SpvExecutionModeEarlyFragmentTests = 9,
+    SpvExecutionModePointMode = 10,
+    SpvExecutionModeXfb = 11,
+    SpvExecutionModeDepthReplacing = 12,
+    SpvExecutionModeDepthGreater = 14,
+    SpvExecutionModeDepthLess = 15,
+    SpvExecutionModeDepthUnchanged = 16,
+    SpvExecutionModeLocalSize = 17,
+    SpvExecutionModeLocalSizeHint = 18,
+    SpvExecutionModeInputPoints = 19,
+    SpvExecutionModeInputLines = 20,
+    SpvExecutionModeInputLinesAdjacency = 21,
+    SpvExecutionModeTriangles = 22,
+    SpvExecutionModeInputTrianglesAdjacency = 23,
+    SpvExecutionModeQuads = 24,
+    SpvExecutionModeIsolines = 25,
+    SpvExecutionModeOutputVertices = 26,
+    SpvExecutionModeOutputPoints = 27,
+    SpvExecutionModeOutputLineStrip = 28,
+    SpvExecutionModeOutputTriangleStrip = 29,
+    SpvExecutionModeVecTypeHint = 30,
+    SpvExecutionModeContractionOff = 31,
+    SpvExecutionModeInitializer = 33,
+    SpvExecutionModeFinalizer = 34,
+    SpvExecutionModeSubgroupSize = 35,
+    SpvExecutionModeSubgroupsPerWorkgroup = 36,
+    SpvExecutionModeSubgroupsPerWorkgroupId = 37,
+    SpvExecutionModeLocalSizeId = 38,
+    SpvExecutionModeLocalSizeHintId = 39,
+    SpvExecutionModeSubgroupUniformControlFlowKHR = 4421,
+    SpvExecutionModePostDepthCoverage = 4446,
+    SpvExecutionModeDenormPreserve = 4459,
+    SpvExecutionModeDenormFlushToZero = 4460,
+    SpvExecutionModeSignedZeroInfNanPreserve = 4461,
+    SpvExecutionModeRoundingModeRTE = 4462,
+    SpvExecutionModeRoundingModeRTZ = 4463,
+    SpvExecutionModeStencilRefReplacingEXT = 5027,
+    SpvExecutionModeOutputLinesNV = 5269,
+    SpvExecutionModeOutputPrimitivesNV = 5270,
+    SpvExecutionModeDerivativeGroupQuadsNV = 5289,
+    SpvExecutionModeDerivativeGroupLinearNV = 5290,
+    SpvExecutionModeOutputTrianglesNV = 5298,
+    SpvExecutionModePixelInterlockOrderedEXT = 5366,
+    SpvExecutionModePixelInterlockUnorderedEXT = 5367,
+    SpvExecutionModeSampleInterlockOrderedEXT = 5368,
+    SpvExecutionModeSampleInterlockUnorderedEXT = 5369,
+    SpvExecutionModeShadingRateInterlockOrderedEXT = 5370,
+    SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371,
+    SpvExecutionModeSharedLocalMemorySizeINTEL = 5618,
+    SpvExecutionModeRoundingModeRTPINTEL = 5620,
+    SpvExecutionModeRoundingModeRTNINTEL = 5621,
+    SpvExecutionModeFloatingPointModeALTINTEL = 5622,
+    SpvExecutionModeFloatingPointModeIEEEINTEL = 5623,
+    SpvExecutionModeMaxWorkgroupSizeINTEL = 5893,
+    SpvExecutionModeMaxWorkDimINTEL = 5894,
+    SpvExecutionModeNoGlobalOffsetINTEL = 5895,
+    SpvExecutionModeNumSIMDWorkitemsINTEL = 5896,
+    SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903,
+    SpvExecutionModeNamedBarrierCountINTEL = 6417,
+    SpvExecutionModeMax = 0x7fffffff,
+} SpvExecutionMode;
+
+typedef enum SpvStorageClass_ {
+    SpvStorageClassUniformConstant = 0,
+    SpvStorageClassInput = 1,
+    SpvStorageClassUniform = 2,
+    SpvStorageClassOutput = 3,
+    SpvStorageClassWorkgroup = 4,
+    SpvStorageClassCrossWorkgroup = 5,
+    SpvStorageClassPrivate = 6,
+    SpvStorageClassFunction = 7,
+    SpvStorageClassGeneric = 8,
+    SpvStorageClassPushConstant = 9,
+    SpvStorageClassAtomicCounter = 10,
+    SpvStorageClassImage = 11,
+    SpvStorageClassStorageBuffer = 12,
+    SpvStorageClassCallableDataKHR = 5328,
+    SpvStorageClassCallableDataNV = 5328,
+    SpvStorageClassIncomingCallableDataKHR = 5329,
+    SpvStorageClassIncomingCallableDataNV = 5329,
+    SpvStorageClassRayPayloadKHR = 5338,
+    SpvStorageClassRayPayloadNV = 5338,
+    SpvStorageClassHitAttributeKHR = 5339,
+    SpvStorageClassHitAttributeNV = 5339,
+    SpvStorageClassIncomingRayPayloadKHR = 5342,
+    SpvStorageClassIncomingRayPayloadNV = 5342,
+    SpvStorageClassShaderRecordBufferKHR = 5343,
+    SpvStorageClassShaderRecordBufferNV = 5343,
+    SpvStorageClassPhysicalStorageBuffer = 5349,
+    SpvStorageClassPhysicalStorageBufferEXT = 5349,
+    SpvStorageClassCodeSectionINTEL = 5605,
+    SpvStorageClassDeviceOnlyINTEL = 5936,
+    SpvStorageClassHostOnlyINTEL = 5937,
+    SpvStorageClassMax = 0x7fffffff,
+} SpvStorageClass;
+
+typedef enum SpvDim_ {
+    SpvDim1D = 0,
+    SpvDim2D = 1,
+    SpvDim3D = 2,
+    SpvDimCube = 3,
+    SpvDimRect = 4,
+    SpvDimBuffer = 5,
+    SpvDimSubpassData = 6,
+    SpvDimMax = 0x7fffffff,
+} SpvDim;
+
+typedef enum SpvSamplerAddressingMode_ {
+    SpvSamplerAddressingModeNone = 0,
+    SpvSamplerAddressingModeClampToEdge = 1,
+    SpvSamplerAddressingModeClamp = 2,
+    SpvSamplerAddressingModeRepeat = 3,
+    SpvSamplerAddressingModeRepeatMirrored = 4,
+    SpvSamplerAddressingModeMax = 0x7fffffff,
+} SpvSamplerAddressingMode;
+
+typedef enum SpvSamplerFilterMode_ {
+    SpvSamplerFilterModeNearest = 0,
+    SpvSamplerFilterModeLinear = 1,
+    SpvSamplerFilterModeMax = 0x7fffffff,
+} SpvSamplerFilterMode;
+
+typedef enum SpvImageFormat_ {
+    SpvImageFormatUnknown = 0,
+    SpvImageFormatRgba32f = 1,
+    SpvImageFormatRgba16f = 2,
+    SpvImageFormatR32f = 3,
+    SpvImageFormatRgba8 = 4,
+    SpvImageFormatRgba8Snorm = 5,
+    SpvImageFormatRg32f = 6,
+    SpvImageFormatRg16f = 7,
+    SpvImageFormatR11fG11fB10f = 8,
+    SpvImageFormatR16f = 9,
+    SpvImageFormatRgba16 = 10,
+    SpvImageFormatRgb10A2 = 11,
+    SpvImageFormatRg16 = 12,
+    SpvImageFormatRg8 = 13,
+    SpvImageFormatR16 = 14,
+    SpvImageFormatR8 = 15,
+    SpvImageFormatRgba16Snorm = 16,
+    SpvImageFormatRg16Snorm = 17,
+    SpvImageFormatRg8Snorm = 18,
+    SpvImageFormatR16Snorm = 19,
+    SpvImageFormatR8Snorm = 20,
+    SpvImageFormatRgba32i = 21,
+    SpvImageFormatRgba16i = 22,
+    SpvImageFormatRgba8i = 23,
+    SpvImageFormatR32i = 24,
+    SpvImageFormatRg32i = 25,
+    SpvImageFormatRg16i = 26,
+    SpvImageFormatRg8i = 27,
+    SpvImageFormatR16i = 28,
+    SpvImageFormatR8i = 29,
+    SpvImageFormatRgba32ui = 30,
+    SpvImageFormatRgba16ui = 31,
+    SpvImageFormatRgba8ui = 32,
+    SpvImageFormatR32ui = 33,
+    SpvImageFormatRgb10a2ui = 34,
+    SpvImageFormatRg32ui = 35,
+    SpvImageFormatRg16ui = 36,
+    SpvImageFormatRg8ui = 37,
+    SpvImageFormatR16ui = 38,
+    SpvImageFormatR8ui = 39,
+    SpvImageFormatR64ui = 40,
+    SpvImageFormatR64i = 41,
+    SpvImageFormatMax = 0x7fffffff,
+} SpvImageFormat;
+
+typedef enum SpvImageChannelOrder_ {
+    SpvImageChannelOrderR = 0,
+    SpvImageChannelOrderA = 1,
+    SpvImageChannelOrderRG = 2,
+    SpvImageChannelOrderRA = 3,
+    SpvImageChannelOrderRGB = 4,
+    SpvImageChannelOrderRGBA = 5,
+    SpvImageChannelOrderBGRA = 6,
+    SpvImageChannelOrderARGB = 7,
+    SpvImageChannelOrderIntensity = 8,
+    SpvImageChannelOrderLuminance = 9,
+    SpvImageChannelOrderRx = 10,
+    SpvImageChannelOrderRGx = 11,
+    SpvImageChannelOrderRGBx = 12,
+    SpvImageChannelOrderDepth = 13,
+    SpvImageChannelOrderDepthStencil = 14,
+    SpvImageChannelOrdersRGB = 15,
+    SpvImageChannelOrdersRGBx = 16,
+    SpvImageChannelOrdersRGBA = 17,
+    SpvImageChannelOrdersBGRA = 18,
+    SpvImageChannelOrderABGR = 19,
+    SpvImageChannelOrderMax = 0x7fffffff,
+} SpvImageChannelOrder;
+
+typedef enum SpvImageChannelDataType_ {
+    SpvImageChannelDataTypeSnormInt8 = 0,
+    SpvImageChannelDataTypeSnormInt16 = 1,
+    SpvImageChannelDataTypeUnormInt8 = 2,
+    SpvImageChannelDataTypeUnormInt16 = 3,
+    SpvImageChannelDataTypeUnormShort565 = 4,
+    SpvImageChannelDataTypeUnormShort555 = 5,
+    SpvImageChannelDataTypeUnormInt101010 = 6,
+    SpvImageChannelDataTypeSignedInt8 = 7,
+    SpvImageChannelDataTypeSignedInt16 = 8,
+    SpvImageChannelDataTypeSignedInt32 = 9,
+    SpvImageChannelDataTypeUnsignedInt8 = 10,
+    SpvImageChannelDataTypeUnsignedInt16 = 11,
+    SpvImageChannelDataTypeUnsignedInt32 = 12,
+    SpvImageChannelDataTypeHalfFloat = 13,
+    SpvImageChannelDataTypeFloat = 14,
+    SpvImageChannelDataTypeUnormInt24 = 15,
+    SpvImageChannelDataTypeUnormInt101010_2 = 16,
+    SpvImageChannelDataTypeMax = 0x7fffffff,
+} SpvImageChannelDataType;
+
+typedef enum SpvImageOperandsShift_ {
+    SpvImageOperandsBiasShift = 0,
+    SpvImageOperandsLodShift = 1,
+    SpvImageOperandsGradShift = 2,
+    SpvImageOperandsConstOffsetShift = 3,
+    SpvImageOperandsOffsetShift = 4,
+    SpvImageOperandsConstOffsetsShift = 5,
+    SpvImageOperandsSampleShift = 6,
+    SpvImageOperandsMinLodShift = 7,
+    SpvImageOperandsMakeTexelAvailableShift = 8,
+    SpvImageOperandsMakeTexelAvailableKHRShift = 8,
+    SpvImageOperandsMakeTexelVisibleShift = 9,
+    SpvImageOperandsMakeTexelVisibleKHRShift = 9,
+    SpvImageOperandsNonPrivateTexelShift = 10,
+    SpvImageOperandsNonPrivateTexelKHRShift = 10,
+    SpvImageOperandsVolatileTexelShift = 11,
+    SpvImageOperandsVolatileTexelKHRShift = 11,
+    SpvImageOperandsSignExtendShift = 12,
+    SpvImageOperandsZeroExtendShift = 13,
+    SpvImageOperandsNontemporalShift = 14,
+    SpvImageOperandsOffsetsShift = 16,
+    SpvImageOperandsMax = 0x7fffffff,
+} SpvImageOperandsShift;
+
+typedef enum SpvImageOperandsMask_ {
+    SpvImageOperandsMaskNone = 0,
+    SpvImageOperandsBiasMask = 0x00000001,
+    SpvImageOperandsLodMask = 0x00000002,
+    SpvImageOperandsGradMask = 0x00000004,
+    SpvImageOperandsConstOffsetMask = 0x00000008,
+    SpvImageOperandsOffsetMask = 0x00000010,
+    SpvImageOperandsConstOffsetsMask = 0x00000020,
+    SpvImageOperandsSampleMask = 0x00000040,
+    SpvImageOperandsMinLodMask = 0x00000080,
+    SpvImageOperandsMakeTexelAvailableMask = 0x00000100,
+    SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
+    SpvImageOperandsMakeTexelVisibleMask = 0x00000200,
+    SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
+    SpvImageOperandsNonPrivateTexelMask = 0x00000400,
+    SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400,
+    SpvImageOperandsVolatileTexelMask = 0x00000800,
+    SpvImageOperandsVolatileTexelKHRMask = 0x00000800,
+    SpvImageOperandsSignExtendMask = 0x00001000,
+    SpvImageOperandsZeroExtendMask = 0x00002000,
+    SpvImageOperandsNontemporalMask = 0x00004000,
+    SpvImageOperandsOffsetsMask = 0x00010000,
+} SpvImageOperandsMask;
+
+typedef enum SpvFPFastMathModeShift_ {
+    SpvFPFastMathModeNotNaNShift = 0,
+    SpvFPFastMathModeNotInfShift = 1,
+    SpvFPFastMathModeNSZShift = 2,
+    SpvFPFastMathModeAllowRecipShift = 3,
+    SpvFPFastMathModeFastShift = 4,
+    SpvFPFastMathModeAllowContractFastINTELShift = 16,
+    SpvFPFastMathModeAllowReassocINTELShift = 17,
+    SpvFPFastMathModeMax = 0x7fffffff,
+} SpvFPFastMathModeShift;
+
+typedef enum SpvFPFastMathModeMask_ {
+    SpvFPFastMathModeMaskNone = 0,
+    SpvFPFastMathModeNotNaNMask = 0x00000001,
+    SpvFPFastMathModeNotInfMask = 0x00000002,
+    SpvFPFastMathModeNSZMask = 0x00000004,
+    SpvFPFastMathModeAllowRecipMask = 0x00000008,
+    SpvFPFastMathModeFastMask = 0x00000010,
+    SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000,
+    SpvFPFastMathModeAllowReassocINTELMask = 0x00020000,
+} SpvFPFastMathModeMask;
+
+typedef enum SpvFPRoundingMode_ {
+    SpvFPRoundingModeRTE = 0,
+    SpvFPRoundingModeRTZ = 1,
+    SpvFPRoundingModeRTP = 2,
+    SpvFPRoundingModeRTN = 3,
+    SpvFPRoundingModeMax = 0x7fffffff,
+} SpvFPRoundingMode;
+
+typedef enum SpvLinkageType_ {
+    SpvLinkageTypeExport = 0,
+    SpvLinkageTypeImport = 1,
+    SpvLinkageTypeLinkOnceODR = 2,
+    SpvLinkageTypeMax = 0x7fffffff,
+} SpvLinkageType;
+
+typedef enum SpvAccessQualifier_ {
+    SpvAccessQualifierReadOnly = 0,
+    SpvAccessQualifierWriteOnly = 1,
+    SpvAccessQualifierReadWrite = 2,
+    SpvAccessQualifierMax = 0x7fffffff,
+} SpvAccessQualifier;
+
+typedef enum SpvFunctionParameterAttribute_ {
+    SpvFunctionParameterAttributeZext = 0,
+    SpvFunctionParameterAttributeSext = 1,
+    SpvFunctionParameterAttributeByVal = 2,
+    SpvFunctionParameterAttributeSret = 3,
+    SpvFunctionParameterAttributeNoAlias = 4,
+    SpvFunctionParameterAttributeNoCapture = 5,
+    SpvFunctionParameterAttributeNoWrite = 6,
+    SpvFunctionParameterAttributeNoReadWrite = 7,
+    SpvFunctionParameterAttributeMax = 0x7fffffff,
+} SpvFunctionParameterAttribute;
+
+typedef enum SpvDecoration_ {
+    SpvDecorationRelaxedPrecision = 0,
+    SpvDecorationSpecId = 1,
+    SpvDecorationBlock = 2,
+    SpvDecorationBufferBlock = 3,
+    SpvDecorationRowMajor = 4,
+    SpvDecorationColMajor = 5,
+    SpvDecorationArrayStride = 6,
+    SpvDecorationMatrixStride = 7,
+    SpvDecorationGLSLShared = 8,
+    SpvDecorationGLSLPacked = 9,
+    SpvDecorationCPacked = 10,
+    SpvDecorationBuiltIn = 11,
+    SpvDecorationNoPerspective = 13,
+    SpvDecorationFlat = 14,
+    SpvDecorationPatch = 15,
+    SpvDecorationCentroid = 16,
+    SpvDecorationSample = 17,
+    SpvDecorationInvariant = 18,
+    SpvDecorationRestrict = 19,
+    SpvDecorationAliased = 20,
+    SpvDecorationVolatile = 21,
+    SpvDecorationConstant = 22,
+    SpvDecorationCoherent = 23,
+    SpvDecorationNonWritable = 24,
+    SpvDecorationNonReadable = 25,
+    SpvDecorationUniform = 26,
+    SpvDecorationUniformId = 27,
+    SpvDecorationSaturatedConversion = 28,
+    SpvDecorationStream = 29,
+    SpvDecorationLocation = 30,
+    SpvDecorationComponent = 31,
+    SpvDecorationIndex = 32,
+    SpvDecorationBinding = 33,
+    SpvDecorationDescriptorSet = 34,
+    SpvDecorationOffset = 35,
+    SpvDecorationXfbBuffer = 36,
+    SpvDecorationXfbStride = 37,
+    SpvDecorationFuncParamAttr = 38,
+    SpvDecorationFPRoundingMode = 39,
+    SpvDecorationFPFastMathMode = 40,
+    SpvDecorationLinkageAttributes = 41,
+    SpvDecorationNoContraction = 42,
+    SpvDecorationInputAttachmentIndex = 43,
+    SpvDecorationAlignment = 44,
+    SpvDecorationMaxByteOffset = 45,
+    SpvDecorationAlignmentId = 46,
+    SpvDecorationMaxByteOffsetId = 47,
+    SpvDecorationNoSignedWrap = 4469,
+    SpvDecorationNoUnsignedWrap = 4470,
+    SpvDecorationExplicitInterpAMD = 4999,
+    SpvDecorationOverrideCoverageNV = 5248,
+    SpvDecorationPassthroughNV = 5250,
+    SpvDecorationViewportRelativeNV = 5252,
+    SpvDecorationSecondaryViewportRelativeNV = 5256,
+    SpvDecorationPerPrimitiveNV = 5271,
+    SpvDecorationPerViewNV = 5272,
+    SpvDecorationPerTaskNV = 5273,
+    SpvDecorationPerVertexKHR = 5285,
+    SpvDecorationPerVertexNV = 5285,
+    SpvDecorationNonUniform = 5300,
+    SpvDecorationNonUniformEXT = 5300,
+    SpvDecorationRestrictPointer = 5355,
+    SpvDecorationRestrictPointerEXT = 5355,
+    SpvDecorationAliasedPointer = 5356,
+    SpvDecorationAliasedPointerEXT = 5356,
+    SpvDecorationBindlessSamplerNV = 5398,
+    SpvDecorationBindlessImageNV = 5399,
+    SpvDecorationBoundSamplerNV = 5400,
+    SpvDecorationBoundImageNV = 5401,
+    SpvDecorationSIMTCallINTEL = 5599,
+    SpvDecorationReferencedIndirectlyINTEL = 5602,
+    SpvDecorationClobberINTEL = 5607,
+    SpvDecorationSideEffectsINTEL = 5608,
+    SpvDecorationVectorComputeVariableINTEL = 5624,
+    SpvDecorationFuncParamIOKindINTEL = 5625,
+    SpvDecorationVectorComputeFunctionINTEL = 5626,
+    SpvDecorationStackCallINTEL = 5627,
+    SpvDecorationGlobalVariableOffsetINTEL = 5628,
+    SpvDecorationCounterBuffer = 5634,
+    SpvDecorationHlslCounterBufferGOOGLE = 5634,
+    SpvDecorationHlslSemanticGOOGLE = 5635,
+    SpvDecorationUserSemantic = 5635,
+    SpvDecorationUserTypeGOOGLE = 5636,
+    SpvDecorationFunctionRoundingModeINTEL = 5822,
+    SpvDecorationFunctionDenormModeINTEL = 5823,
+    SpvDecorationRegisterINTEL = 5825,
+    SpvDecorationMemoryINTEL = 5826,
+    SpvDecorationNumbanksINTEL = 5827,
+    SpvDecorationBankwidthINTEL = 5828,
+    SpvDecorationMaxPrivateCopiesINTEL = 5829,
+    SpvDecorationSinglepumpINTEL = 5830,
+    SpvDecorationDoublepumpINTEL = 5831,
+    SpvDecorationMaxReplicatesINTEL = 5832,
+    SpvDecorationSimpleDualPortINTEL = 5833,
+    SpvDecorationMergeINTEL = 5834,
+    SpvDecorationBankBitsINTEL = 5835,
+    SpvDecorationForcePow2DepthINTEL = 5836,
+    SpvDecorationBurstCoalesceINTEL = 5899,
+    SpvDecorationCacheSizeINTEL = 5900,
+    SpvDecorationDontStaticallyCoalesceINTEL = 5901,
+    SpvDecorationPrefetchINTEL = 5902,
+    SpvDecorationStallEnableINTEL = 5905,
+    SpvDecorationFuseLoopsInFunctionINTEL = 5907,
+    SpvDecorationAliasScopeINTEL = 5914,
+    SpvDecorationNoAliasINTEL = 5915,
+    SpvDecorationBufferLocationINTEL = 5921,
+    SpvDecorationIOPipeStorageINTEL = 5944,
+    SpvDecorationFunctionFloatingPointModeINTEL = 6080,
+    SpvDecorationSingleElementVectorINTEL = 6085,
+    SpvDecorationVectorComputeCallableFunctionINTEL = 6087,
+    SpvDecorationMediaBlockIOINTEL = 6140,
+    SpvDecorationMax = 0x7fffffff,
+} SpvDecoration;
+
+typedef enum SpvBuiltIn_ {
+    SpvBuiltInPosition = 0,
+    SpvBuiltInPointSize = 1,
+    SpvBuiltInClipDistance = 3,
+    SpvBuiltInCullDistance = 4,
+    SpvBuiltInVertexId = 5,
+    SpvBuiltInInstanceId = 6,
+    SpvBuiltInPrimitiveId = 7,
+    SpvBuiltInInvocationId = 8,
+    SpvBuiltInLayer = 9,
+    SpvBuiltInViewportIndex = 10,
+    SpvBuiltInTessLevelOuter = 11,
+    SpvBuiltInTessLevelInner = 12,
+    SpvBuiltInTessCoord = 13,
+    SpvBuiltInPatchVertices = 14,
+    SpvBuiltInFragCoord = 15,
+    SpvBuiltInPointCoord = 16,
+    SpvBuiltInFrontFacing = 17,
+    SpvBuiltInSampleId = 18,
+    SpvBuiltInSamplePosition = 19,
+    SpvBuiltInSampleMask = 20,
+    SpvBuiltInFragDepth = 22,
+    SpvBuiltInHelperInvocation = 23,
+    SpvBuiltInNumWorkgroups = 24,
+    SpvBuiltInWorkgroupSize = 25,
+    SpvBuiltInWorkgroupId = 26,
+    SpvBuiltInLocalInvocationId = 27,
+    SpvBuiltInGlobalInvocationId = 28,
+    SpvBuiltInLocalInvocationIndex = 29,
+    SpvBuiltInWorkDim = 30,
+    SpvBuiltInGlobalSize = 31,
+    SpvBuiltInEnqueuedWorkgroupSize = 32,
+    SpvBuiltInGlobalOffset = 33,
+    SpvBuiltInGlobalLinearId = 34,
+    SpvBuiltInSubgroupSize = 36,
+    SpvBuiltInSubgroupMaxSize = 37,
+    SpvBuiltInNumSubgroups = 38,
+    SpvBuiltInNumEnqueuedSubgroups = 39,
+    SpvBuiltInSubgroupId = 40,
+    SpvBuiltInSubgroupLocalInvocationId = 41,
+    SpvBuiltInVertexIndex = 42,
+    SpvBuiltInInstanceIndex = 43,
+    SpvBuiltInSubgroupEqMask = 4416,
+    SpvBuiltInSubgroupEqMaskKHR = 4416,
+    SpvBuiltInSubgroupGeMask = 4417,
+    SpvBuiltInSubgroupGeMaskKHR = 4417,
+    SpvBuiltInSubgroupGtMask = 4418,
+    SpvBuiltInSubgroupGtMaskKHR = 4418,
+    SpvBuiltInSubgroupLeMask = 4419,
+    SpvBuiltInSubgroupLeMaskKHR = 4419,
+    SpvBuiltInSubgroupLtMask = 4420,
+    SpvBuiltInSubgroupLtMaskKHR = 4420,
+    SpvBuiltInBaseVertex = 4424,
+    SpvBuiltInBaseInstance = 4425,
+    SpvBuiltInDrawIndex = 4426,
+    SpvBuiltInPrimitiveShadingRateKHR = 4432,
+    SpvBuiltInDeviceIndex = 4438,
+    SpvBuiltInViewIndex = 4440,
+    SpvBuiltInShadingRateKHR = 4444,
+    SpvBuiltInBaryCoordNoPerspAMD = 4992,
+    SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993,
+    SpvBuiltInBaryCoordNoPerspSampleAMD = 4994,
+    SpvBuiltInBaryCoordSmoothAMD = 4995,
+    SpvBuiltInBaryCoordSmoothCentroidAMD = 4996,
+    SpvBuiltInBaryCoordSmoothSampleAMD = 4997,
+    SpvBuiltInBaryCoordPullModelAMD = 4998,
+    SpvBuiltInFragStencilRefEXT = 5014,
+    SpvBuiltInViewportMaskNV = 5253,
+    SpvBuiltInSecondaryPositionNV = 5257,
+    SpvBuiltInSecondaryViewportMaskNV = 5258,
+    SpvBuiltInPositionPerViewNV = 5261,
+    SpvBuiltInViewportMaskPerViewNV = 5262,
+    SpvBuiltInFullyCoveredEXT = 5264,
+    SpvBuiltInTaskCountNV = 5274,
+    SpvBuiltInPrimitiveCountNV = 5275,
+    SpvBuiltInPrimitiveIndicesNV = 5276,
+    SpvBuiltInClipDistancePerViewNV = 5277,
+    SpvBuiltInCullDistancePerViewNV = 5278,
+    SpvBuiltInLayerPerViewNV = 5279,
+    SpvBuiltInMeshViewCountNV = 5280,
+    SpvBuiltInMeshViewIndicesNV = 5281,
+    SpvBuiltInBaryCoordKHR = 5286,
+    SpvBuiltInBaryCoordNV = 5286,
+    SpvBuiltInBaryCoordNoPerspKHR = 5287,
+    SpvBuiltInBaryCoordNoPerspNV = 5287,
+    SpvBuiltInFragSizeEXT = 5292,
+    SpvBuiltInFragmentSizeNV = 5292,
+    SpvBuiltInFragInvocationCountEXT = 5293,
+    SpvBuiltInInvocationsPerPixelNV = 5293,
+    SpvBuiltInLaunchIdKHR = 5319,
+    SpvBuiltInLaunchIdNV = 5319,
+    SpvBuiltInLaunchSizeKHR = 5320,
+    SpvBuiltInLaunchSizeNV = 5320,
+    SpvBuiltInWorldRayOriginKHR = 5321,
+    SpvBuiltInWorldRayOriginNV = 5321,
+    SpvBuiltInWorldRayDirectionKHR = 5322,
+    SpvBuiltInWorldRayDirectionNV = 5322,
+    SpvBuiltInObjectRayOriginKHR = 5323,
+    SpvBuiltInObjectRayOriginNV = 5323,
+    SpvBuiltInObjectRayDirectionKHR = 5324,
+    SpvBuiltInObjectRayDirectionNV = 5324,
+    SpvBuiltInRayTminKHR = 5325,
+    SpvBuiltInRayTminNV = 5325,
+    SpvBuiltInRayTmaxKHR = 5326,
+    SpvBuiltInRayTmaxNV = 5326,
+    SpvBuiltInInstanceCustomIndexKHR = 5327,
+    SpvBuiltInInstanceCustomIndexNV = 5327,
+    SpvBuiltInObjectToWorldKHR = 5330,
+    SpvBuiltInObjectToWorldNV = 5330,
+    SpvBuiltInWorldToObjectKHR = 5331,
+    SpvBuiltInWorldToObjectNV = 5331,
+    SpvBuiltInHitTNV = 5332,
+    SpvBuiltInHitKindKHR = 5333,
+    SpvBuiltInHitKindNV = 5333,
+    SpvBuiltInCurrentRayTimeNV = 5334,
+    SpvBuiltInIncomingRayFlagsKHR = 5351,
+    SpvBuiltInIncomingRayFlagsNV = 5351,
+    SpvBuiltInRayGeometryIndexKHR = 5352,
+    SpvBuiltInWarpsPerSMNV = 5374,
+    SpvBuiltInSMCountNV = 5375,
+    SpvBuiltInWarpIDNV = 5376,
+    SpvBuiltInSMIDNV = 5377,
+    SpvBuiltInCullMaskKHR = 6021,
+    SpvBuiltInMax = 0x7fffffff,
+} SpvBuiltIn;
+
+typedef enum SpvSelectionControlShift_ {
+    SpvSelectionControlFlattenShift = 0,
+    SpvSelectionControlDontFlattenShift = 1,
+    SpvSelectionControlMax = 0x7fffffff,
+} SpvSelectionControlShift;
+
+typedef enum SpvSelectionControlMask_ {
+    SpvSelectionControlMaskNone = 0,
+    SpvSelectionControlFlattenMask = 0x00000001,
+    SpvSelectionControlDontFlattenMask = 0x00000002,
+} SpvSelectionControlMask;
+
+typedef enum SpvLoopControlShift_ {
+    SpvLoopControlUnrollShift = 0,
+    SpvLoopControlDontUnrollShift = 1,
+    SpvLoopControlDependencyInfiniteShift = 2,
+    SpvLoopControlDependencyLengthShift = 3,
+    SpvLoopControlMinIterationsShift = 4,
+    SpvLoopControlMaxIterationsShift = 5,
+    SpvLoopControlIterationMultipleShift = 6,
+    SpvLoopControlPeelCountShift = 7,
+    SpvLoopControlPartialCountShift = 8,
+    SpvLoopControlInitiationIntervalINTELShift = 16,
+    SpvLoopControlMaxConcurrencyINTELShift = 17,
+    SpvLoopControlDependencyArrayINTELShift = 18,
+    SpvLoopControlPipelineEnableINTELShift = 19,
+    SpvLoopControlLoopCoalesceINTELShift = 20,
+    SpvLoopControlMaxInterleavingINTELShift = 21,
+    SpvLoopControlSpeculatedIterationsINTELShift = 22,
+    SpvLoopControlNoFusionINTELShift = 23,
+    SpvLoopControlMax = 0x7fffffff,
+} SpvLoopControlShift;
+
+typedef enum SpvLoopControlMask_ {
+    SpvLoopControlMaskNone = 0,
+    SpvLoopControlUnrollMask = 0x00000001,
+    SpvLoopControlDontUnrollMask = 0x00000002,
+    SpvLoopControlDependencyInfiniteMask = 0x00000004,
+    SpvLoopControlDependencyLengthMask = 0x00000008,
+    SpvLoopControlMinIterationsMask = 0x00000010,
+    SpvLoopControlMaxIterationsMask = 0x00000020,
+    SpvLoopControlIterationMultipleMask = 0x00000040,
+    SpvLoopControlPeelCountMask = 0x00000080,
+    SpvLoopControlPartialCountMask = 0x00000100,
+    SpvLoopControlInitiationIntervalINTELMask = 0x00010000,
+    SpvLoopControlMaxConcurrencyINTELMask = 0x00020000,
+    SpvLoopControlDependencyArrayINTELMask = 0x00040000,
+    SpvLoopControlPipelineEnableINTELMask = 0x00080000,
+    SpvLoopControlLoopCoalesceINTELMask = 0x00100000,
+    SpvLoopControlMaxInterleavingINTELMask = 0x00200000,
+    SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000,
+    SpvLoopControlNoFusionINTELMask = 0x00800000,
+} SpvLoopControlMask;
+
+typedef enum SpvFunctionControlShift_ {
+    SpvFunctionControlInlineShift = 0,
+    SpvFunctionControlDontInlineShift = 1,
+    SpvFunctionControlPureShift = 2,
+    SpvFunctionControlConstShift = 3,
+    SpvFunctionControlOptNoneINTELShift = 16,
+    SpvFunctionControlMax = 0x7fffffff,
+} SpvFunctionControlShift;
+
+typedef enum SpvFunctionControlMask_ {
+    SpvFunctionControlMaskNone = 0,
+    SpvFunctionControlInlineMask = 0x00000001,
+    SpvFunctionControlDontInlineMask = 0x00000002,
+    SpvFunctionControlPureMask = 0x00000004,
+    SpvFunctionControlConstMask = 0x00000008,
+    SpvFunctionControlOptNoneINTELMask = 0x00010000,
+} SpvFunctionControlMask;
+
+typedef enum SpvMemorySemanticsShift_ {
+    SpvMemorySemanticsAcquireShift = 1,
+    SpvMemorySemanticsReleaseShift = 2,
+    SpvMemorySemanticsAcquireReleaseShift = 3,
+    SpvMemorySemanticsSequentiallyConsistentShift = 4,
+    SpvMemorySemanticsUniformMemoryShift = 6,
+    SpvMemorySemanticsSubgroupMemoryShift = 7,
+    SpvMemorySemanticsWorkgroupMemoryShift = 8,
+    SpvMemorySemanticsCrossWorkgroupMemoryShift = 9,
+    SpvMemorySemanticsAtomicCounterMemoryShift = 10,
+    SpvMemorySemanticsImageMemoryShift = 11,
+    SpvMemorySemanticsOutputMemoryShift = 12,
+    SpvMemorySemanticsOutputMemoryKHRShift = 12,
+    SpvMemorySemanticsMakeAvailableShift = 13,
+    SpvMemorySemanticsMakeAvailableKHRShift = 13,
+    SpvMemorySemanticsMakeVisibleShift = 14,
+    SpvMemorySemanticsMakeVisibleKHRShift = 14,
+    SpvMemorySemanticsVolatileShift = 15,
+    SpvMemorySemanticsMax = 0x7fffffff,
+} SpvMemorySemanticsShift;
+
+typedef enum SpvMemorySemanticsMask_ {
+    SpvMemorySemanticsMaskNone = 0,
+    SpvMemorySemanticsAcquireMask = 0x00000002,
+    SpvMemorySemanticsReleaseMask = 0x00000004,
+    SpvMemorySemanticsAcquireReleaseMask = 0x00000008,
+    SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010,
+    SpvMemorySemanticsUniformMemoryMask = 0x00000040,
+    SpvMemorySemanticsSubgroupMemoryMask = 0x00000080,
+    SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100,
+    SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+    SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+    SpvMemorySemanticsImageMemoryMask = 0x00000800,
+    SpvMemorySemanticsOutputMemoryMask = 0x00001000,
+    SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000,
+    SpvMemorySemanticsMakeAvailableMask = 0x00002000,
+    SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000,
+    SpvMemorySemanticsMakeVisibleMask = 0x00004000,
+    SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000,
+    SpvMemorySemanticsVolatileMask = 0x00008000,
+} SpvMemorySemanticsMask;
+
+typedef enum SpvMemoryAccessShift_ {
+    SpvMemoryAccessVolatileShift = 0,
+    SpvMemoryAccessAlignedShift = 1,
+    SpvMemoryAccessNontemporalShift = 2,
+    SpvMemoryAccessMakePointerAvailableShift = 3,
+    SpvMemoryAccessMakePointerAvailableKHRShift = 3,
+    SpvMemoryAccessMakePointerVisibleShift = 4,
+    SpvMemoryAccessMakePointerVisibleKHRShift = 4,
+    SpvMemoryAccessNonPrivatePointerShift = 5,
+    SpvMemoryAccessNonPrivatePointerKHRShift = 5,
+    SpvMemoryAccessAliasScopeINTELMaskShift = 16,
+    SpvMemoryAccessNoAliasINTELMaskShift = 17,
+    SpvMemoryAccessMax = 0x7fffffff,
+} SpvMemoryAccessShift;
+
+typedef enum SpvMemoryAccessMask_ {
+    SpvMemoryAccessMaskNone = 0,
+    SpvMemoryAccessVolatileMask = 0x00000001,
+    SpvMemoryAccessAlignedMask = 0x00000002,
+    SpvMemoryAccessNontemporalMask = 0x00000004,
+    SpvMemoryAccessMakePointerAvailableMask = 0x00000008,
+    SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008,
+    SpvMemoryAccessMakePointerVisibleMask = 0x00000010,
+    SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010,
+    SpvMemoryAccessNonPrivatePointerMask = 0x00000020,
+    SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020,
+    SpvMemoryAccessAliasScopeINTELMaskMask = 0x00010000,
+    SpvMemoryAccessNoAliasINTELMaskMask = 0x00020000,
+} SpvMemoryAccessMask;
+
+typedef enum SpvScope_ {
+    SpvScopeCrossDevice = 0,
+    SpvScopeDevice = 1,
+    SpvScopeWorkgroup = 2,
+    SpvScopeSubgroup = 3,
+    SpvScopeInvocation = 4,
+    SpvScopeQueueFamily = 5,
+    SpvScopeQueueFamilyKHR = 5,
+    SpvScopeShaderCallKHR = 6,
+    SpvScopeMax = 0x7fffffff,
+} SpvScope;
+
+typedef enum SpvGroupOperation_ {
+    SpvGroupOperationReduce = 0,
+    SpvGroupOperationInclusiveScan = 1,
+    SpvGroupOperationExclusiveScan = 2,
+    SpvGroupOperationClusteredReduce = 3,
+    SpvGroupOperationPartitionedReduceNV = 6,
+    SpvGroupOperationPartitionedInclusiveScanNV = 7,
+    SpvGroupOperationPartitionedExclusiveScanNV = 8,
+    SpvGroupOperationMax = 0x7fffffff,
+} SpvGroupOperation;
+
+typedef enum SpvKernelEnqueueFlags_ {
+    SpvKernelEnqueueFlagsNoWait = 0,
+    SpvKernelEnqueueFlagsWaitKernel = 1,
+    SpvKernelEnqueueFlagsWaitWorkGroup = 2,
+    SpvKernelEnqueueFlagsMax = 0x7fffffff,
+} SpvKernelEnqueueFlags;
+
+typedef enum SpvKernelProfilingInfoShift_ {
+    SpvKernelProfilingInfoCmdExecTimeShift = 0,
+    SpvKernelProfilingInfoMax = 0x7fffffff,
+} SpvKernelProfilingInfoShift;
+
+typedef enum SpvKernelProfilingInfoMask_ {
+    SpvKernelProfilingInfoMaskNone = 0,
+    SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001,
+} SpvKernelProfilingInfoMask;
+
+typedef enum SpvCapability_ {
+    SpvCapabilityMatrix = 0,
+    SpvCapabilityShader = 1,
+    SpvCapabilityGeometry = 2,
+    SpvCapabilityTessellation = 3,
+    SpvCapabilityAddresses = 4,
+    SpvCapabilityLinkage = 5,
+    SpvCapabilityKernel = 6,
+    SpvCapabilityVector16 = 7,
+    SpvCapabilityFloat16Buffer = 8,
+    SpvCapabilityFloat16 = 9,
+    SpvCapabilityFloat64 = 10,
+    SpvCapabilityInt64 = 11,
+    SpvCapabilityInt64Atomics = 12,
+    SpvCapabilityImageBasic = 13,
+    SpvCapabilityImageReadWrite = 14,
+    SpvCapabilityImageMipmap = 15,
+    SpvCapabilityPipes = 17,
+    SpvCapabilityGroups = 18,
+    SpvCapabilityDeviceEnqueue = 19,
+    SpvCapabilityLiteralSampler = 20,
+    SpvCapabilityAtomicStorage = 21,
+    SpvCapabilityInt16 = 22,
+    SpvCapabilityTessellationPointSize = 23,
+    SpvCapabilityGeometryPointSize = 24,
+    SpvCapabilityImageGatherExtended = 25,
+    SpvCapabilityStorageImageMultisample = 27,
+    SpvCapabilityUniformBufferArrayDynamicIndexing = 28,
+    SpvCapabilitySampledImageArrayDynamicIndexing = 29,
+    SpvCapabilityStorageBufferArrayDynamicIndexing = 30,
+    SpvCapabilityStorageImageArrayDynamicIndexing = 31,
+    SpvCapabilityClipDistance = 32,
+    SpvCapabilityCullDistance = 33,
+    SpvCapabilityImageCubeArray = 34,
+    SpvCapabilitySampleRateShading = 35,
+    SpvCapabilityImageRect = 36,
+    SpvCapabilitySampledRect = 37,
+    SpvCapabilityGenericPointer = 38,
+    SpvCapabilityInt8 = 39,
+    SpvCapabilityInputAttachment = 40,
+    SpvCapabilitySparseResidency = 41,
+    SpvCapabilityMinLod = 42,
+    SpvCapabilitySampled1D = 43,
+    SpvCapabilityImage1D = 44,
+    SpvCapabilitySampledCubeArray = 45,
+    SpvCapabilitySampledBuffer = 46,
+    SpvCapabilityImageBuffer = 47,
+    SpvCapabilityImageMSArray = 48,
+    SpvCapabilityStorageImageExtendedFormats = 49,
+    SpvCapabilityImageQuery = 50,
+    SpvCapabilityDerivativeControl = 51,
+    SpvCapabilityInterpolationFunction = 52,
+    SpvCapabilityTransformFeedback = 53,
+    SpvCapabilityGeometryStreams = 54,
+    SpvCapabilityStorageImageReadWithoutFormat = 55,
+    SpvCapabilityStorageImageWriteWithoutFormat = 56,
+    SpvCapabilityMultiViewport = 57,
+    SpvCapabilitySubgroupDispatch = 58,
+    SpvCapabilityNamedBarrier = 59,
+    SpvCapabilityPipeStorage = 60,
+    SpvCapabilityGroupNonUniform = 61,
+    SpvCapabilityGroupNonUniformVote = 62,
+    SpvCapabilityGroupNonUniformArithmetic = 63,
+    SpvCapabilityGroupNonUniformBallot = 64,
+    SpvCapabilityGroupNonUniformShuffle = 65,
+    SpvCapabilityGroupNonUniformShuffleRelative = 66,
+    SpvCapabilityGroupNonUniformClustered = 67,
+    SpvCapabilityGroupNonUniformQuad = 68,
+    SpvCapabilityShaderLayer = 69,
+    SpvCapabilityShaderViewportIndex = 70,
+    SpvCapabilityUniformDecoration = 71,
+    SpvCapabilityFragmentShadingRateKHR = 4422,
+    SpvCapabilitySubgroupBallotKHR = 4423,
+    SpvCapabilityDrawParameters = 4427,
+    SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428,
+    SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429,
+    SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430,
+    SpvCapabilitySubgroupVoteKHR = 4431,
+    SpvCapabilityStorageBuffer16BitAccess = 4433,
+    SpvCapabilityStorageUniformBufferBlock16 = 4433,
+    SpvCapabilityStorageUniform16 = 4434,
+    SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434,
+    SpvCapabilityStoragePushConstant16 = 4435,
+    SpvCapabilityStorageInputOutput16 = 4436,
+    SpvCapabilityDeviceGroup = 4437,
+    SpvCapabilityMultiView = 4439,
+    SpvCapabilityVariablePointersStorageBuffer = 4441,
+    SpvCapabilityVariablePointers = 4442,
+    SpvCapabilityAtomicStorageOps = 4445,
+    SpvCapabilitySampleMaskPostDepthCoverage = 4447,
+    SpvCapabilityStorageBuffer8BitAccess = 4448,
+    SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449,
+    SpvCapabilityStoragePushConstant8 = 4450,
+    SpvCapabilityDenormPreserve = 4464,
+    SpvCapabilityDenormFlushToZero = 4465,
+    SpvCapabilitySignedZeroInfNanPreserve = 4466,
+    SpvCapabilityRoundingModeRTE = 4467,
+    SpvCapabilityRoundingModeRTZ = 4468,
+    SpvCapabilityRayQueryProvisionalKHR = 4471,
+    SpvCapabilityRayQueryKHR = 4472,
+    SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478,
+    SpvCapabilityRayTracingKHR = 4479,
+    SpvCapabilityFloat16ImageAMD = 5008,
+    SpvCapabilityImageGatherBiasLodAMD = 5009,
+    SpvCapabilityFragmentMaskAMD = 5010,
+    SpvCapabilityStencilExportEXT = 5013,
+    SpvCapabilityImageReadWriteLodAMD = 5015,
+    SpvCapabilityInt64ImageEXT = 5016,
+    SpvCapabilityShaderClockKHR = 5055,
+    SpvCapabilitySampleMaskOverrideCoverageNV = 5249,
+    SpvCapabilityGeometryShaderPassthroughNV = 5251,
+    SpvCapabilityShaderViewportIndexLayerEXT = 5254,
+    SpvCapabilityShaderViewportIndexLayerNV = 5254,
+    SpvCapabilityShaderViewportMaskNV = 5255,
+    SpvCapabilityShaderStereoViewNV = 5259,
+    SpvCapabilityPerViewAttributesNV = 5260,
+    SpvCapabilityFragmentFullyCoveredEXT = 5265,
+    SpvCapabilityMeshShadingNV = 5266,
+    SpvCapabilityImageFootprintNV = 5282,
+    SpvCapabilityFragmentBarycentricKHR = 5284,
+    SpvCapabilityFragmentBarycentricNV = 5284,
+    SpvCapabilityComputeDerivativeGroupQuadsNV = 5288,
+    SpvCapabilityFragmentDensityEXT = 5291,
+    SpvCapabilityShadingRateNV = 5291,
+    SpvCapabilityGroupNonUniformPartitionedNV = 5297,
+    SpvCapabilityShaderNonUniform = 5301,
+    SpvCapabilityShaderNonUniformEXT = 5301,
+    SpvCapabilityRuntimeDescriptorArray = 5302,
+    SpvCapabilityRuntimeDescriptorArrayEXT = 5302,
+    SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303,
+    SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+    SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304,
+    SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+    SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305,
+    SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+    SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306,
+    SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+    SpvCapabilitySampledImageArrayNonUniformIndexing = 5307,
+    SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+    SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308,
+    SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+    SpvCapabilityStorageImageArrayNonUniformIndexing = 5309,
+    SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+    SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310,
+    SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+    SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311,
+    SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+    SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312,
+    SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
+    SpvCapabilityRayTracingNV = 5340,
+    SpvCapabilityRayTracingMotionBlurNV = 5341,
+    SpvCapabilityVulkanMemoryModel = 5345,
+    SpvCapabilityVulkanMemoryModelKHR = 5345,
+    SpvCapabilityVulkanMemoryModelDeviceScope = 5346,
+    SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
+    SpvCapabilityPhysicalStorageBufferAddresses = 5347,
+    SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347,
+    SpvCapabilityComputeDerivativeGroupLinearNV = 5350,
+    SpvCapabilityRayTracingProvisionalKHR = 5353,
+    SpvCapabilityCooperativeMatrixNV = 5357,
+    SpvCapabilityFragmentShaderSampleInterlockEXT = 5363,
+    SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372,
+    SpvCapabilityShaderSMBuiltinsNV = 5373,
+    SpvCapabilityFragmentShaderPixelInterlockEXT = 5378,
+    SpvCapabilityDemoteToHelperInvocation = 5379,
+    SpvCapabilityDemoteToHelperInvocationEXT = 5379,
+    SpvCapabilityBindlessTextureNV = 5390,
+    SpvCapabilitySubgroupShuffleINTEL = 5568,
+    SpvCapabilitySubgroupBufferBlockIOINTEL = 5569,
+    SpvCapabilitySubgroupImageBlockIOINTEL = 5570,
+    SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+    SpvCapabilityRoundToInfinityINTEL = 5582,
+    SpvCapabilityFloatingPointModeINTEL = 5583,
+    SpvCapabilityIntegerFunctions2INTEL = 5584,
+    SpvCapabilityFunctionPointersINTEL = 5603,
+    SpvCapabilityIndirectReferencesINTEL = 5604,
+    SpvCapabilityAsmINTEL = 5606,
+    SpvCapabilityAtomicFloat32MinMaxEXT = 5612,
+    SpvCapabilityAtomicFloat64MinMaxEXT = 5613,
+    SpvCapabilityAtomicFloat16MinMaxEXT = 5616,
+    SpvCapabilityVectorComputeINTEL = 5617,
+    SpvCapabilityVectorAnyINTEL = 5619,
+    SpvCapabilityExpectAssumeKHR = 5629,
+    SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696,
+    SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697,
+    SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698,
+    SpvCapabilityVariableLengthArrayINTEL = 5817,
+    SpvCapabilityFunctionFloatControlINTEL = 5821,
+    SpvCapabilityFPGAMemoryAttributesINTEL = 5824,
+    SpvCapabilityFPFastMathModeINTEL = 5837,
+    SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844,
+    SpvCapabilityArbitraryPrecisionFloatingPointINTEL = 5845,
+    SpvCapabilityUnstructuredLoopControlsINTEL = 5886,
+    SpvCapabilityFPGALoopControlsINTEL = 5888,
+    SpvCapabilityKernelAttributesINTEL = 5892,
+    SpvCapabilityFPGAKernelAttributesINTEL = 5897,
+    SpvCapabilityFPGAMemoryAccessesINTEL = 5898,
+    SpvCapabilityFPGAClusterAttributesINTEL = 5904,
+    SpvCapabilityLoopFuseINTEL = 5906,
+    SpvCapabilityMemoryAccessAliasingINTEL = 5910,
+    SpvCapabilityFPGABufferLocationINTEL = 5920,
+    SpvCapabilityArbitraryPrecisionFixedPointINTEL = 5922,
+    SpvCapabilityUSMStorageClassesINTEL = 5935,
+    SpvCapabilityIOPipesINTEL = 5943,
+    SpvCapabilityBlockingPipesINTEL = 5945,
+    SpvCapabilityFPGARegINTEL = 5948,
+    SpvCapabilityDotProductInputAll = 6016,
+    SpvCapabilityDotProductInputAllKHR = 6016,
+    SpvCapabilityDotProductInput4x8Bit = 6017,
+    SpvCapabilityDotProductInput4x8BitKHR = 6017,
+    SpvCapabilityDotProductInput4x8BitPacked = 6018,
+    SpvCapabilityDotProductInput4x8BitPackedKHR = 6018,
+    SpvCapabilityDotProduct = 6019,
+    SpvCapabilityDotProductKHR = 6019,
+    SpvCapabilityRayCullMaskKHR = 6020,
+    SpvCapabilityBitInstructions = 6025,
+    SpvCapabilityGroupNonUniformRotateKHR = 6026,
+    SpvCapabilityAtomicFloat32AddEXT = 6033,
+    SpvCapabilityAtomicFloat64AddEXT = 6034,
+    SpvCapabilityLongConstantCompositeINTEL = 6089,
+    SpvCapabilityOptNoneINTEL = 6094,
+    SpvCapabilityAtomicFloat16AddEXT = 6095,
+    SpvCapabilityDebugInfoModuleINTEL = 6114,
+    SpvCapabilitySplitBarrierINTEL = 6141,
+    SpvCapabilityGroupUniformArithmeticKHR = 6400,
+    SpvCapabilityMax = 0x7fffffff,
+} SpvCapability;
+
+typedef enum SpvRayFlagsShift_ {
+    SpvRayFlagsOpaqueKHRShift = 0,
+    SpvRayFlagsNoOpaqueKHRShift = 1,
+    SpvRayFlagsTerminateOnFirstHitKHRShift = 2,
+    SpvRayFlagsSkipClosestHitShaderKHRShift = 3,
+    SpvRayFlagsCullBackFacingTrianglesKHRShift = 4,
+    SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5,
+    SpvRayFlagsCullOpaqueKHRShift = 6,
+    SpvRayFlagsCullNoOpaqueKHRShift = 7,
+    SpvRayFlagsSkipTrianglesKHRShift = 8,
+    SpvRayFlagsSkipAABBsKHRShift = 9,
+    SpvRayFlagsMax = 0x7fffffff,
+} SpvRayFlagsShift;
+
+typedef enum SpvRayFlagsMask_ {
+    SpvRayFlagsMaskNone = 0,
+    SpvRayFlagsOpaqueKHRMask = 0x00000001,
+    SpvRayFlagsNoOpaqueKHRMask = 0x00000002,
+    SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004,
+    SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008,
+    SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010,
+    SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020,
+    SpvRayFlagsCullOpaqueKHRMask = 0x00000040,
+    SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080,
+    SpvRayFlagsSkipTrianglesKHRMask = 0x00000100,
+    SpvRayFlagsSkipAABBsKHRMask = 0x00000200,
+} SpvRayFlagsMask;
+
+typedef enum SpvRayQueryIntersection_ {
+    SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0,
+    SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1,
+    SpvRayQueryIntersectionMax = 0x7fffffff,
+} SpvRayQueryIntersection;
+
+typedef enum SpvRayQueryCommittedIntersectionType_ {
+    SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0,
+    SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1,
+    SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2,
+    SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff,
+} SpvRayQueryCommittedIntersectionType;
+
+typedef enum SpvRayQueryCandidateIntersectionType_ {
+    SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0,
+    SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1,
+    SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff,
+} SpvRayQueryCandidateIntersectionType;
+
+typedef enum SpvFragmentShadingRateShift_ {
+    SpvFragmentShadingRateVertical2PixelsShift = 0,
+    SpvFragmentShadingRateVertical4PixelsShift = 1,
+    SpvFragmentShadingRateHorizontal2PixelsShift = 2,
+    SpvFragmentShadingRateHorizontal4PixelsShift = 3,
+    SpvFragmentShadingRateMax = 0x7fffffff,
+} SpvFragmentShadingRateShift;
+
+typedef enum SpvFragmentShadingRateMask_ {
+    SpvFragmentShadingRateMaskNone = 0,
+    SpvFragmentShadingRateVertical2PixelsMask = 0x00000001,
+    SpvFragmentShadingRateVertical4PixelsMask = 0x00000002,
+    SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004,
+    SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008,
+} SpvFragmentShadingRateMask;
+
+typedef enum SpvFPDenormMode_ {
+    SpvFPDenormModePreserve = 0,
+    SpvFPDenormModeFlushToZero = 1,
+    SpvFPDenormModeMax = 0x7fffffff,
+} SpvFPDenormMode;
+
+typedef enum SpvFPOperationMode_ {
+    SpvFPOperationModeIEEE = 0,
+    SpvFPOperationModeALT = 1,
+    SpvFPOperationModeMax = 0x7fffffff,
+} SpvFPOperationMode;
+
+typedef enum SpvQuantizationModes_ {
+    SpvQuantizationModesTRN = 0,
+    SpvQuantizationModesTRN_ZERO = 1,
+    SpvQuantizationModesRND = 2,
+    SpvQuantizationModesRND_ZERO = 3,
+    SpvQuantizationModesRND_INF = 4,
+    SpvQuantizationModesRND_MIN_INF = 5,
+    SpvQuantizationModesRND_CONV = 6,
+    SpvQuantizationModesRND_CONV_ODD = 7,
+    SpvQuantizationModesMax = 0x7fffffff,
+} SpvQuantizationModes;
+
+typedef enum SpvOverflowModes_ {
+    SpvOverflowModesWRAP = 0,
+    SpvOverflowModesSAT = 1,
+    SpvOverflowModesSAT_ZERO = 2,
+    SpvOverflowModesSAT_SYM = 3,
+    SpvOverflowModesMax = 0x7fffffff,
+} SpvOverflowModes;
+
+typedef enum SpvPackedVectorFormat_ {
+    SpvPackedVectorFormatPackedVectorFormat4x8Bit = 0,
+    SpvPackedVectorFormatPackedVectorFormat4x8BitKHR = 0,
+    SpvPackedVectorFormatMax = 0x7fffffff,
+} SpvPackedVectorFormat;
+
+typedef 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,
+    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,
+    SpvOpCopyLogical = 400,
+    SpvOpPtrEqual = 401,
+    SpvOpPtrNotEqual = 402,
+    SpvOpPtrDiff = 403,
+    SpvOpTerminateInvocation = 4416,
+    SpvOpSubgroupBallotKHR = 4421,
+    SpvOpSubgroupFirstInvocationKHR = 4422,
+    SpvOpSubgroupAllKHR = 4428,
+    SpvOpSubgroupAnyKHR = 4429,
+    SpvOpSubgroupAllEqualKHR = 4430,
+    SpvOpGroupNonUniformRotateKHR = 4431,
+    SpvOpSubgroupReadInvocationKHR = 4432,
+    SpvOpTraceRayKHR = 4445,
+    SpvOpExecuteCallableKHR = 4446,
+    SpvOpConvertUToAccelerationStructureKHR = 4447,
+    SpvOpIgnoreIntersectionKHR = 4448,
+    SpvOpTerminateRayKHR = 4449,
+    SpvOpSDot = 4450,
+    SpvOpSDotKHR = 4450,
+    SpvOpUDot = 4451,
+    SpvOpUDotKHR = 4451,
+    SpvOpSUDot = 4452,
+    SpvOpSUDotKHR = 4452,
+    SpvOpSDotAccSat = 4453,
+    SpvOpSDotAccSatKHR = 4453,
+    SpvOpUDotAccSat = 4454,
+    SpvOpUDotAccSatKHR = 4454,
+    SpvOpSUDotAccSat = 4455,
+    SpvOpSUDotAccSatKHR = 4455,
+    SpvOpTypeRayQueryKHR = 4472,
+    SpvOpRayQueryInitializeKHR = 4473,
+    SpvOpRayQueryTerminateKHR = 4474,
+    SpvOpRayQueryGenerateIntersectionKHR = 4475,
+    SpvOpRayQueryConfirmIntersectionKHR = 4476,
+    SpvOpRayQueryProceedKHR = 4477,
+    SpvOpRayQueryGetIntersectionTypeKHR = 4479,
+    SpvOpGroupIAddNonUniformAMD = 5000,
+    SpvOpGroupFAddNonUniformAMD = 5001,
+    SpvOpGroupFMinNonUniformAMD = 5002,
+    SpvOpGroupUMinNonUniformAMD = 5003,
+    SpvOpGroupSMinNonUniformAMD = 5004,
+    SpvOpGroupFMaxNonUniformAMD = 5005,
+    SpvOpGroupUMaxNonUniformAMD = 5006,
+    SpvOpGroupSMaxNonUniformAMD = 5007,
+    SpvOpFragmentMaskFetchAMD = 5011,
+    SpvOpFragmentFetchAMD = 5012,
+    SpvOpReadClockKHR = 5056,
+    SpvOpImageSampleFootprintNV = 5283,
+    SpvOpGroupNonUniformPartitionNV = 5296,
+    SpvOpWritePackedPrimitiveIndices4x8NV = 5299,
+    SpvOpReportIntersectionKHR = 5334,
+    SpvOpReportIntersectionNV = 5334,
+    SpvOpIgnoreIntersectionNV = 5335,
+    SpvOpTerminateRayNV = 5336,
+    SpvOpTraceNV = 5337,
+    SpvOpTraceMotionNV = 5338,
+    SpvOpTraceRayMotionNV = 5339,
+    SpvOpTypeAccelerationStructureKHR = 5341,
+    SpvOpTypeAccelerationStructureNV = 5341,
+    SpvOpExecuteCallableNV = 5344,
+    SpvOpTypeCooperativeMatrixNV = 5358,
+    SpvOpCooperativeMatrixLoadNV = 5359,
+    SpvOpCooperativeMatrixStoreNV = 5360,
+    SpvOpCooperativeMatrixMulAddNV = 5361,
+    SpvOpCooperativeMatrixLengthNV = 5362,
+    SpvOpBeginInvocationInterlockEXT = 5364,
+    SpvOpEndInvocationInterlockEXT = 5365,
+    SpvOpDemoteToHelperInvocation = 5380,
+    SpvOpDemoteToHelperInvocationEXT = 5380,
+    SpvOpIsHelperInvocationEXT = 5381,
+    SpvOpConvertUToImageNV = 5391,
+    SpvOpConvertUToSamplerNV = 5392,
+    SpvOpConvertImageToUNV = 5393,
+    SpvOpConvertSamplerToUNV = 5394,
+    SpvOpConvertUToSampledImageNV = 5395,
+    SpvOpConvertSampledImageToUNV = 5396,
+    SpvOpSamplerImageAddressingModeNV = 5397,
+    SpvOpSubgroupShuffleINTEL = 5571,
+    SpvOpSubgroupShuffleDownINTEL = 5572,
+    SpvOpSubgroupShuffleUpINTEL = 5573,
+    SpvOpSubgroupShuffleXorINTEL = 5574,
+    SpvOpSubgroupBlockReadINTEL = 5575,
+    SpvOpSubgroupBlockWriteINTEL = 5576,
+    SpvOpSubgroupImageBlockReadINTEL = 5577,
+    SpvOpSubgroupImageBlockWriteINTEL = 5578,
+    SpvOpSubgroupImageMediaBlockReadINTEL = 5580,
+    SpvOpSubgroupImageMediaBlockWriteINTEL = 5581,
+    SpvOpUCountLeadingZerosINTEL = 5585,
+    SpvOpUCountTrailingZerosINTEL = 5586,
+    SpvOpAbsISubINTEL = 5587,
+    SpvOpAbsUSubINTEL = 5588,
+    SpvOpIAddSatINTEL = 5589,
+    SpvOpUAddSatINTEL = 5590,
+    SpvOpIAverageINTEL = 5591,
+    SpvOpUAverageINTEL = 5592,
+    SpvOpIAverageRoundedINTEL = 5593,
+    SpvOpUAverageRoundedINTEL = 5594,
+    SpvOpISubSatINTEL = 5595,
+    SpvOpUSubSatINTEL = 5596,
+    SpvOpIMul32x16INTEL = 5597,
+    SpvOpUMul32x16INTEL = 5598,
+    SpvOpConstantFunctionPointerINTEL = 5600,
+    SpvOpFunctionPointerCallINTEL = 5601,
+    SpvOpAsmTargetINTEL = 5609,
+    SpvOpAsmINTEL = 5610,
+    SpvOpAsmCallINTEL = 5611,
+    SpvOpAtomicFMinEXT = 5614,
+    SpvOpAtomicFMaxEXT = 5615,
+    SpvOpAssumeTrueKHR = 5630,
+    SpvOpExpectKHR = 5631,
+    SpvOpDecorateString = 5632,
+    SpvOpDecorateStringGOOGLE = 5632,
+    SpvOpMemberDecorateString = 5633,
+    SpvOpMemberDecorateStringGOOGLE = 5633,
+    SpvOpVmeImageINTEL = 5699,
+    SpvOpTypeVmeImageINTEL = 5700,
+    SpvOpTypeAvcImePayloadINTEL = 5701,
+    SpvOpTypeAvcRefPayloadINTEL = 5702,
+    SpvOpTypeAvcSicPayloadINTEL = 5703,
+    SpvOpTypeAvcMcePayloadINTEL = 5704,
+    SpvOpTypeAvcMceResultINTEL = 5705,
+    SpvOpTypeAvcImeResultINTEL = 5706,
+    SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707,
+    SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708,
+    SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709,
+    SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710,
+    SpvOpTypeAvcRefResultINTEL = 5711,
+    SpvOpTypeAvcSicResultINTEL = 5712,
+    SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713,
+    SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714,
+    SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715,
+    SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716,
+    SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717,
+    SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718,
+    SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719,
+    SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720,
+    SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721,
+    SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722,
+    SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723,
+    SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724,
+    SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725,
+    SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726,
+    SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727,
+    SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728,
+    SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729,
+    SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730,
+    SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731,
+    SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732,
+    SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733,
+    SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734,
+    SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735,
+    SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736,
+    SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737,
+    SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738,
+    SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739,
+    SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740,
+    SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741,
+    SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742,
+    SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743,
+    SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744,
+    SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745,
+    SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746,
+    SpvOpSubgroupAvcImeInitializeINTEL = 5747,
+    SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748,
+    SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749,
+    SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750,
+    SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751,
+    SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752,
+    SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753,
+    SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754,
+    SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755,
+    SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756,
+    SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757,
+    SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758,
+    SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759,
+    SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760,
+    SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761,
+    SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762,
+    SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763,
+    SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764,
+    SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765,
+    SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766,
+    SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767,
+    SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768,
+    SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769,
+    SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770,
+    SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771,
+    SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772,
+    SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773,
+    SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774,
+    SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775,
+    SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776,
+    SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777,
+    SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778,
+    SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779,
+    SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780,
+    SpvOpSubgroupAvcFmeInitializeINTEL = 5781,
+    SpvOpSubgroupAvcBmeInitializeINTEL = 5782,
+    SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783,
+    SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784,
+    SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785,
+    SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786,
+    SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787,
+    SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788,
+    SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789,
+    SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790,
+    SpvOpSubgroupAvcSicInitializeINTEL = 5791,
+    SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792,
+    SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793,
+    SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794,
+    SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795,
+    SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796,
+    SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797,
+    SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798,
+    SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799,
+    SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800,
+    SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801,
+    SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802,
+    SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803,
+    SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804,
+    SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805,
+    SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806,
+    SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807,
+    SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808,
+    SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809,
+    SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810,
+    SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811,
+    SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812,
+    SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813,
+    SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814,
+    SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815,
+    SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816,
+    SpvOpVariableLengthArrayINTEL = 5818,
+    SpvOpSaveMemoryINTEL = 5819,
+    SpvOpRestoreMemoryINTEL = 5820,
+    SpvOpArbitraryFloatSinCosPiINTEL = 5840,
+    SpvOpArbitraryFloatCastINTEL = 5841,
+    SpvOpArbitraryFloatCastFromIntINTEL = 5842,
+    SpvOpArbitraryFloatCastToIntINTEL = 5843,
+    SpvOpArbitraryFloatAddINTEL = 5846,
+    SpvOpArbitraryFloatSubINTEL = 5847,
+    SpvOpArbitraryFloatMulINTEL = 5848,
+    SpvOpArbitraryFloatDivINTEL = 5849,
+    SpvOpArbitraryFloatGTINTEL = 5850,
+    SpvOpArbitraryFloatGEINTEL = 5851,
+    SpvOpArbitraryFloatLTINTEL = 5852,
+    SpvOpArbitraryFloatLEINTEL = 5853,
+    SpvOpArbitraryFloatEQINTEL = 5854,
+    SpvOpArbitraryFloatRecipINTEL = 5855,
+    SpvOpArbitraryFloatRSqrtINTEL = 5856,
+    SpvOpArbitraryFloatCbrtINTEL = 5857,
+    SpvOpArbitraryFloatHypotINTEL = 5858,
+    SpvOpArbitraryFloatSqrtINTEL = 5859,
+    SpvOpArbitraryFloatLogINTEL = 5860,
+    SpvOpArbitraryFloatLog2INTEL = 5861,
+    SpvOpArbitraryFloatLog10INTEL = 5862,
+    SpvOpArbitraryFloatLog1pINTEL = 5863,
+    SpvOpArbitraryFloatExpINTEL = 5864,
+    SpvOpArbitraryFloatExp2INTEL = 5865,
+    SpvOpArbitraryFloatExp10INTEL = 5866,
+    SpvOpArbitraryFloatExpm1INTEL = 5867,
+    SpvOpArbitraryFloatSinINTEL = 5868,
+    SpvOpArbitraryFloatCosINTEL = 5869,
+    SpvOpArbitraryFloatSinCosINTEL = 5870,
+    SpvOpArbitraryFloatSinPiINTEL = 5871,
+    SpvOpArbitraryFloatCosPiINTEL = 5872,
+    SpvOpArbitraryFloatASinINTEL = 5873,
+    SpvOpArbitraryFloatASinPiINTEL = 5874,
+    SpvOpArbitraryFloatACosINTEL = 5875,
+    SpvOpArbitraryFloatACosPiINTEL = 5876,
+    SpvOpArbitraryFloatATanINTEL = 5877,
+    SpvOpArbitraryFloatATanPiINTEL = 5878,
+    SpvOpArbitraryFloatATan2INTEL = 5879,
+    SpvOpArbitraryFloatPowINTEL = 5880,
+    SpvOpArbitraryFloatPowRINTEL = 5881,
+    SpvOpArbitraryFloatPowNINTEL = 5882,
+    SpvOpLoopControlINTEL = 5887,
+    SpvOpAliasDomainDeclINTEL = 5911,
+    SpvOpAliasScopeDeclINTEL = 5912,
+    SpvOpAliasScopeListDeclINTEL = 5913,
+    SpvOpFixedSqrtINTEL = 5923,
+    SpvOpFixedRecipINTEL = 5924,
+    SpvOpFixedRsqrtINTEL = 5925,
+    SpvOpFixedSinINTEL = 5926,
+    SpvOpFixedCosINTEL = 5927,
+    SpvOpFixedSinCosINTEL = 5928,
+    SpvOpFixedSinPiINTEL = 5929,
+    SpvOpFixedCosPiINTEL = 5930,
+    SpvOpFixedSinCosPiINTEL = 5931,
+    SpvOpFixedLogINTEL = 5932,
+    SpvOpFixedExpINTEL = 5933,
+    SpvOpPtrCastToCrossWorkgroupINTEL = 5934,
+    SpvOpCrossWorkgroupCastToPtrINTEL = 5938,
+    SpvOpReadPipeBlockingINTEL = 5946,
+    SpvOpWritePipeBlockingINTEL = 5947,
+    SpvOpFPGARegINTEL = 5949,
+    SpvOpRayQueryGetRayTMinKHR = 6016,
+    SpvOpRayQueryGetRayFlagsKHR = 6017,
+    SpvOpRayQueryGetIntersectionTKHR = 6018,
+    SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019,
+    SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020,
+    SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021,
+    SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022,
+    SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023,
+    SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024,
+    SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025,
+    SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026,
+    SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027,
+    SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028,
+    SpvOpRayQueryGetWorldRayDirectionKHR = 6029,
+    SpvOpRayQueryGetWorldRayOriginKHR = 6030,
+    SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031,
+    SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032,
+    SpvOpAtomicFAddEXT = 6035,
+    SpvOpTypeBufferSurfaceINTEL = 6086,
+    SpvOpTypeStructContinuedINTEL = 6090,
+    SpvOpConstantCompositeContinuedINTEL = 6091,
+    SpvOpSpecConstantCompositeContinuedINTEL = 6092,
+    SpvOpControlBarrierArriveINTEL = 6142,
+    SpvOpControlBarrierWaitINTEL = 6143,
+    SpvOpGroupIMulKHR = 6401,
+    SpvOpGroupFMulKHR = 6402,
+    SpvOpGroupBitwiseAndKHR = 6403,
+    SpvOpGroupBitwiseOrKHR = 6404,
+    SpvOpGroupBitwiseXorKHR = 6405,
+    SpvOpGroupLogicalAndKHR = 6406,
+    SpvOpGroupLogicalOrKHR = 6407,
+    SpvOpGroupLogicalXorKHR = 6408,
+    SpvOpMax = 0x7fffffff,
+} SpvOp;
+
+#ifdef SPV_ENABLE_UTILITY_CODE
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) {
+    *hasResult = *hasResultType = false;
+    switch (opcode) {
+    default: /* unknown opcode */ break;
+    case SpvOpNop: *hasResult = false; *hasResultType = false; break;
+    case SpvOpUndef: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSource: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break;
+    case SpvOpName: *hasResult = false; *hasResultType = false; break;
+    case SpvOpMemberName: *hasResult = false; *hasResultType = false; break;
+    case SpvOpString: *hasResult = true; *hasResultType = false; break;
+    case SpvOpLine: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExtension: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break;
+    case SpvOpExtInst: *hasResult = true; *hasResultType = true; break;
+    case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCapability: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break;
+    case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstant: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFunction: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break;
+    case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVariable: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLoad: *hasResult = true; *hasResultType = true; break;
+    case SpvOpStore: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break;
+    case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break;
+    case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break;
+    case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDecorate: *hasResult = false; *hasResultType = false; break;
+    case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break;
+    case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break;
+    case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break;
+    case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTranspose: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageGather: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageRead: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break;
+    case SpvOpImage: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUConvert: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSConvert: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFConvert: *hasResult = true; *hasResultType = true; break;
+    case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitcast: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSNegate: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFNegate: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpISub: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFSub: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIMul: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFMul: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUDiv: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSDiv: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFDiv: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUMod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSRem: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSMod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFRem: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFMod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break;
+    case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break;
+    case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break;
+    case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break;
+    case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break;
+    case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAny: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAll: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIsNan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIsInf: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break;
+    case SpvOpOrdered: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUnordered: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSelect: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpULessThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break;
+    case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break;
+    case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpNot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBitCount: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdx: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdy: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFwidth: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break;
+    case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break;
+    case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break;
+    case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break;
+    case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break;
+    case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPhi: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break;
+    case SpvOpLabel: *hasResult = true; *hasResultType = false; break;
+    case SpvOpBranch: *hasResult = false; *hasResultType = false; break;
+    case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSwitch: *hasResult = false; *hasResultType = false; break;
+    case SpvOpKill: *hasResult = false; *hasResultType = false; break;
+    case SpvOpReturn: *hasResult = false; *hasResultType = false; break;
+    case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break;
+    case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break;
+    case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break;
+    case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break;
+    case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break;
+    case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break;
+    case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break;
+    case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break;
+    case SpvOpNoLine: *hasResult = false; *hasResultType = false; break;
+    case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break;
+    case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break;
+    case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break;
+    case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break;
+    case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break;
+    case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break;
+    case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTraceRayKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTerminateRayKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSDot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUDot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSUDot: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSDotAccSat: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUDotAccSat: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSUDotAccSat: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break;
+    case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpReportIntersectionNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTraceMotionNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break;
+    case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break;
+    case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break;
+    case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break;
+    case SpvOpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break;
+    case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertUToImageNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertImageToUNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAsmCallINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break;
+    case SpvOpExpectKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break;
+    case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break;
+    case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedSinINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedCosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedLogINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFixedExpINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpFPGARegINTEL: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break;
+    case SpvOpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break;
+    case SpvOpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break;
+    case SpvOpGroupIMulKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupFMulKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break;
+    case SpvOpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break;
+    }
+}
+#endif /* SPV_ENABLE_UTILITY_CODE */
+
+#endif
diff --git a/cbits/spirv_reflect.c b/cbits/spirv_reflect.c
new file mode 100644
--- /dev/null
+++ b/cbits/spirv_reflect.c
@@ -0,0 +1,5097 @@
+/*
+ Copyright 2017-2022 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "spirv_reflect.h"
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#if defined(WIN32)
+  #define _CRTDBG_MAP_ALLOC
+  #include <stdlib.h>
+  #include <crtdbg.h>
+#else
+  #include <stdlib.h>
+#endif
+
+#if defined(SPIRV_REFLECT_ENABLE_ASSERTS)
+  #define SPV_REFLECT_ASSERT(COND) \
+    assert(COND);
+#else
+#define SPV_REFLECT_ASSERT(COND)
+#endif
+
+// Temporary enums until these make it into SPIR-V/Vulkan
+// clang-format off
+enum {
+  SpvReflectOpDecorateId                      = 332,
+  SpvReflectOpDecorateStringGOOGLE            = 5632,
+  SpvReflectOpMemberDecorateStringGOOGLE      = 5633,
+  SpvReflectDecorationHlslCounterBufferGOOGLE = 5634,
+  SpvReflectDecorationHlslSemanticGOOGLE      = 5635
+};
+// clang-format on
+
+// clang-format off
+enum {
+  SPIRV_STARTING_WORD_INDEX       = 5,
+  SPIRV_WORD_SIZE                 = sizeof(uint32_t),
+  SPIRV_BYTE_WIDTH                = 8,
+  SPIRV_MINIMUM_FILE_SIZE         = SPIRV_STARTING_WORD_INDEX * SPIRV_WORD_SIZE,
+  SPIRV_DATA_ALIGNMENT            = 4 * SPIRV_WORD_SIZE, // 16
+  SPIRV_ACCESS_CHAIN_INDEX_OFFSET = 4,
+};
+// clang-format on
+
+// clang-format off
+enum {
+  INVALID_VALUE  = 0xFFFFFFFF,
+};
+// clang-format on
+
+// clang-format off
+enum {
+  MAX_NODE_NAME_LENGTH      = 1024,
+};
+// clang-format on
+
+// clang-format off
+enum {
+  IMAGE_SAMPLED = 1,
+  IMAGE_STORAGE = 2
+};
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvArrayTraits {
+  uint32_t                        element_type_id;
+  uint32_t                        length_id;
+} SpvReflectPrvArrayTraits;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvImageTraits {
+  uint32_t                        sampled_type_id;
+  SpvDim                          dim;
+  uint32_t                        depth;
+  uint32_t                        arrayed;
+  uint32_t                        ms;
+  uint32_t                        sampled;
+  SpvImageFormat                  image_format;
+} SpvReflectPrvImageTraits;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvNumberDecoration {
+  uint32_t                        word_offset;
+  uint32_t                        value;
+} SpvReflectPrvNumberDecoration;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvStringDecoration {
+  uint32_t                        word_offset;
+  const char*                     value;
+} SpvReflectPrvStringDecoration;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvDecorations {
+  bool                            is_relaxed_precision;
+  bool                            is_block;
+  bool                            is_buffer_block;
+  bool                            is_row_major;
+  bool                            is_column_major;
+  bool                            is_built_in;
+  bool                            is_noperspective;
+  bool                            is_flat;
+  bool                            is_non_writable;
+  bool                            is_non_readable;
+  SpvReflectPrvNumberDecoration   set;
+  SpvReflectPrvNumberDecoration   binding;
+  SpvReflectPrvNumberDecoration   input_attachment_index;
+  SpvReflectPrvNumberDecoration   location;
+  SpvReflectPrvNumberDecoration   offset;
+  SpvReflectPrvNumberDecoration   uav_counter_buffer;
+  SpvReflectPrvStringDecoration   semantic;
+  uint32_t                        array_stride;
+  uint32_t                        matrix_stride;
+  SpvBuiltIn                      built_in;
+} SpvReflectPrvDecorations;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvNode {
+  uint32_t                        result_id;
+  SpvOp                           op;
+  uint32_t                        result_type_id;
+  uint32_t                        type_id;
+  SpvCapability                   capability;
+  SpvStorageClass                 storage_class;
+  uint32_t                        word_offset;
+  uint32_t                        word_count;
+  bool                            is_type;
+
+  SpvReflectPrvArrayTraits        array_traits;
+  SpvReflectPrvImageTraits        image_traits;
+  uint32_t                        image_type_id;
+
+  const char*                     name;
+  SpvReflectPrvDecorations        decorations;
+  uint32_t                        member_count;
+  const char**                    member_names;
+  SpvReflectPrvDecorations*       member_decorations;
+} SpvReflectPrvNode;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvString {
+  uint32_t                        result_id;
+  const char*                     string;
+} SpvReflectPrvString;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvFunction {
+  uint32_t                        id;
+  uint32_t                        callee_count;
+  uint32_t*                       callees;
+  struct SpvReflectPrvFunction**  callee_ptrs;
+  uint32_t                        accessed_ptr_count;
+  uint32_t*                       accessed_ptrs;
+} SpvReflectPrvFunction;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvAccessChain {
+  uint32_t                        result_id;
+  uint32_t                        result_type_id;
+  //
+  // Pointing to the base of a composite object.
+  // Generally the id of descriptor block variable
+  uint32_t                        base_id;
+  // 
+  // From spec:
+  //   The first index in Indexes will select the 
+  //   top-level member/element/component/element 
+  //   of the base composite
+  uint32_t                        index_count;
+  uint32_t*                       indexes;
+} SpvReflectPrvAccessChain;
+// clang-format on
+
+// clang-format off
+typedef struct SpvReflectPrvParser {
+  size_t                          spirv_word_count;
+  uint32_t*                       spirv_code;
+  uint32_t                        string_count;
+  SpvReflectPrvString*            strings;
+  SpvSourceLanguage               source_language;
+  uint32_t                        source_language_version;
+  uint32_t                        source_file_id;
+  const char*                     source_embedded;
+  size_t                          node_count;
+  SpvReflectPrvNode*              nodes;
+  uint32_t                        entry_point_count;
+  uint32_t                        capability_count;
+  uint32_t                        function_count;
+  SpvReflectPrvFunction*          functions;
+  uint32_t                        access_chain_count;
+  SpvReflectPrvAccessChain*       access_chains;
+
+  uint32_t                        type_count;
+  uint32_t                        descriptor_count;
+  uint32_t                        push_constant_count;
+} SpvReflectPrvParser;
+// clang-format on
+
+static uint32_t Max(
+   uint32_t a, 
+   uint32_t b)
+{
+  return a > b ? a : b;
+}
+
+static uint32_t RoundUp(
+   uint32_t value,
+   uint32_t multiple)
+{
+  assert(multiple && ((multiple & (multiple - 1)) == 0));
+  return (value + multiple - 1) & ~(multiple - 1);
+}
+
+#define IsNull(ptr) \
+  (ptr == NULL)
+
+#define IsNotNull(ptr) \
+  (ptr != NULL)
+
+#define SafeFree(ptr)  \
+  {                    \
+     free((void*)ptr); \
+     ptr = NULL;       \
+  }
+
+static int SortCompareUint32(
+  const void* a, 
+  const void* b)
+{
+  const uint32_t* p_a = (const uint32_t*)a;
+  const uint32_t* p_b = (const uint32_t*)b;
+
+  return (int)*p_a - (int)*p_b;
+}
+
+//
+// De-duplicates a sorted array and returns the new size.
+//
+// Note: The array doesn't actually need to be sorted, just
+// arranged into "runs" so that all the entries with one
+// value are adjacent.
+//
+static size_t DedupSortedUint32(uint32_t* arr, size_t size)
+{
+  if (size == 0) {
+    return 0;
+  }
+  size_t dedup_idx = 0;
+  for (size_t i = 0; i < size; ++i) {
+    if (arr[dedup_idx] != arr[i]) {
+      ++dedup_idx;
+      arr[dedup_idx] = arr[i];
+    }
+  }
+  return dedup_idx+1;
+}
+
+static bool SearchSortedUint32(
+  const uint32_t* arr, 
+  size_t          size, 
+  uint32_t        target)
+{
+  size_t lo = 0;
+  size_t hi = size;
+  while (lo < hi) {
+    size_t mid = (hi - lo) / 2 + lo;
+    if (arr[mid] == target) {
+      return true;
+    } else if (arr[mid] < target) {
+      lo = mid+1;
+    } else {
+      hi = mid;
+    }
+  }
+  return false;
+}
+
+static SpvReflectResult IntersectSortedUint32(
+  const uint32_t* p_arr0, 
+  size_t          arr0_size,
+  const uint32_t* p_arr1, 
+  size_t          arr1_size,
+  uint32_t**      pp_res,
+  size_t*         res_size
+)
+{
+  *pp_res = NULL;
+  *res_size = 0;
+  if (IsNull(p_arr0) || IsNull(p_arr1)) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  const uint32_t* arr0_end = p_arr0 + arr0_size;
+  const uint32_t* arr1_end = p_arr1 + arr1_size;
+
+  const uint32_t* idx0 = p_arr0;
+  const uint32_t* idx1 = p_arr1;
+  while (idx0 != arr0_end && idx1 != arr1_end) {
+    if (*idx0 < *idx1) {
+      ++idx0;
+    } else if (*idx0 > *idx1) {
+      ++idx1;
+    } else {
+      ++*res_size;
+      ++idx0;
+      ++idx1;
+    }
+  }
+
+  if (*res_size > 0) {
+    *pp_res = (uint32_t*)calloc(*res_size, sizeof(**pp_res));
+    if (IsNull(*pp_res)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+    uint32_t* idxr = *pp_res;
+    idx0 = p_arr0;
+    idx1 = p_arr1;
+    while (idx0 != arr0_end && idx1 != arr1_end) {
+      if (*idx0 < *idx1) {
+        ++idx0;
+      } else if (*idx0 > *idx1) {
+        ++idx1;
+      } else {
+        *(idxr++) = *idx0;
+        ++idx0;
+        ++idx1;
+      }
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+
+static bool InRange(
+  const SpvReflectPrvParser* p_parser, 
+  uint32_t                   index)
+{
+  bool in_range = false;
+  if (IsNotNull(p_parser)) {
+    in_range = (index < p_parser->spirv_word_count);
+  }
+  return in_range;
+}
+
+static SpvReflectResult ReadU32(
+  SpvReflectPrvParser* p_parser,
+  uint32_t             word_offset,
+  uint32_t*            p_value)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(InRange(p_parser, word_offset));
+  SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF;
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && InRange(p_parser, word_offset)) {
+    *p_value = *(p_parser->spirv_code + word_offset);
+    result = SPV_REFLECT_RESULT_SUCCESS;
+  }
+  return result;
+}
+
+#define CHECKED_READU32(parser, word_offset, value)                                      \
+  {                                                                                      \
+    SpvReflectResult checked_readu32_result = ReadU32(parser,                            \
+                                                      word_offset, (uint32_t*)&(value)); \
+    if (checked_readu32_result != SPV_REFLECT_RESULT_SUCCESS) {                          \
+      return checked_readu32_result;                                                     \
+    }                                                                                    \
+  }
+
+#define CHECKED_READU32_CAST(parser, word_offset, cast_to_type, value)         \
+  {                                                                            \
+    uint32_t checked_readu32_cast_u32 = UINT32_MAX;                            \
+    SpvReflectResult checked_readu32_cast_result = ReadU32(parser,             \
+                                      word_offset,                             \
+                                      (uint32_t*)&(checked_readu32_cast_u32)); \
+    if (checked_readu32_cast_result != SPV_REFLECT_RESULT_SUCCESS) {           \
+      return checked_readu32_cast_result;                                      \
+    }                                                                          \
+    value = (cast_to_type)checked_readu32_cast_u32;                            \
+  }
+
+#define IF_READU32(result, parser, word_offset, value)          \
+  if ((result) == SPV_REFLECT_RESULT_SUCCESS) {                 \
+    result = ReadU32(parser, word_offset, (uint32_t*)&(value)); \
+  }
+
+#define IF_READU32_CAST(result, parser, word_offset, cast_to_type, value) \
+  if ((result) == SPV_REFLECT_RESULT_SUCCESS) {                           \
+    uint32_t if_readu32_cast_u32 = UINT32_MAX;                            \
+    result = ReadU32(parser, word_offset, &if_readu32_cast_u32);          \
+    if ((result) == SPV_REFLECT_RESULT_SUCCESS) {                         \
+      value = (cast_to_type)if_readu32_cast_u32;                          \
+    }                                                                     \
+  }
+
+static SpvReflectResult ReadStr(
+  SpvReflectPrvParser* p_parser, 
+  uint32_t             word_offset, 
+  uint32_t             word_index, 
+  uint32_t             word_count,
+  uint32_t*            p_buf_size, 
+  char*                p_buf
+)
+{
+  uint32_t limit = (word_offset + word_count);
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(InRange(p_parser, limit));
+  SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF;
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && InRange(p_parser, limit)) {
+    const char* c_str = (const char*)(p_parser->spirv_code + word_offset + word_index);
+    uint32_t n = word_count * SPIRV_WORD_SIZE;
+    uint32_t length_with_terminator = 0;
+    for (uint32_t i = 0; i < n; ++i) {
+      char c = *(c_str + i);
+      if (c == 0) {
+        length_with_terminator = i + 1;
+        break;
+      }
+    }
+
+    if (length_with_terminator > 0) {
+      result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+      if (IsNotNull(p_buf_size) && IsNotNull(p_buf)) {
+        result = SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED;
+        if (length_with_terminator <= *p_buf_size) {
+          memset(p_buf, 0, *p_buf_size);
+          memcpy(p_buf, c_str, length_with_terminator);
+          result = SPV_REFLECT_RESULT_SUCCESS;
+        }
+      }
+      else {
+        if (IsNotNull(p_buf_size)) {
+          *p_buf_size = length_with_terminator;
+          result = SPV_REFLECT_RESULT_SUCCESS;
+        }
+      }
+    }
+  }
+  return result;
+}
+
+static SpvReflectDecorationFlags ApplyDecorations(const SpvReflectPrvDecorations* p_decoration_fields)
+{
+  SpvReflectDecorationFlags decorations = SPV_REFLECT_DECORATION_NONE;
+  if (p_decoration_fields->is_relaxed_precision) {
+    decorations |= SPV_REFLECT_DECORATION_RELAXED_PRECISION;
+  }
+  if (p_decoration_fields->is_block) {
+    decorations |= SPV_REFLECT_DECORATION_BLOCK;
+  }
+  if (p_decoration_fields->is_buffer_block) {
+    decorations |= SPV_REFLECT_DECORATION_BUFFER_BLOCK;
+  }
+  if (p_decoration_fields->is_row_major) {
+    decorations |= SPV_REFLECT_DECORATION_ROW_MAJOR;
+  }
+  if (p_decoration_fields->is_column_major) {
+    decorations |= SPV_REFLECT_DECORATION_COLUMN_MAJOR;
+  }
+  if (p_decoration_fields->is_built_in) {
+    decorations |= SPV_REFLECT_DECORATION_BUILT_IN;
+  }
+  if (p_decoration_fields->is_noperspective) {
+    decorations |= SPV_REFLECT_DECORATION_NOPERSPECTIVE;
+  }
+  if (p_decoration_fields->is_flat) {
+    decorations |= SPV_REFLECT_DECORATION_FLAT;
+  }
+  if (p_decoration_fields->is_non_writable) {
+    decorations |= SPV_REFLECT_DECORATION_NON_WRITABLE;
+  }
+  if (p_decoration_fields->is_non_readable) {
+    decorations |= SPV_REFLECT_DECORATION_NON_READABLE;
+  }
+  return decorations;
+}
+
+static void ApplyNumericTraits(const SpvReflectTypeDescription* p_type, SpvReflectNumericTraits* p_numeric_traits)
+{
+  memcpy(p_numeric_traits, &p_type->traits.numeric, sizeof(p_type->traits.numeric));
+}
+
+static void ApplyArrayTraits(const SpvReflectTypeDescription* p_type, SpvReflectArrayTraits* p_array_traits)
+{
+  memcpy(p_array_traits, &p_type->traits.array, sizeof(p_type->traits.array));
+}
+
+static SpvReflectPrvNode* FindNode(
+  SpvReflectPrvParser* p_parser,
+  uint32_t             result_id)
+{
+  SpvReflectPrvNode* p_node = NULL;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_elem = &(p_parser->nodes[i]);
+    if (p_elem->result_id == result_id) {
+      p_node = p_elem;
+      break;
+    }
+  }
+  return p_node;
+}
+
+static SpvReflectTypeDescription* FindType(SpvReflectShaderModule* p_module, uint32_t type_id)
+{
+  SpvReflectTypeDescription* p_type = NULL;
+  for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) {
+    SpvReflectTypeDescription* p_elem = &(p_module->_internal->type_descriptions[i]);
+    if (p_elem->id == type_id) {
+      p_type = p_elem;
+      break;
+    }
+  }
+  return p_type;
+}
+
+static SpvReflectResult CreateParser(
+  size_t               size, 
+  void*                p_code, 
+  SpvReflectPrvParser* p_parser)
+{
+  if (p_code == NULL) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (size < SPIRV_MINIMUM_FILE_SIZE) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE;
+  }
+  if ((size % 4) != 0) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE;
+  }
+
+  p_parser->spirv_word_count = size / SPIRV_WORD_SIZE;
+  p_parser->spirv_code = (uint32_t*)p_code;
+
+  if (p_parser->spirv_code[0] != SpvMagicNumber) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_MAGIC_NUMBER;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static void DestroyParser(SpvReflectPrvParser* p_parser)
+{
+  if (!IsNull(p_parser->nodes)) {
+    // Free nodes
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if (IsNotNull(p_node->member_names)) {
+        SafeFree(p_node->member_names);
+      }
+      if (IsNotNull(p_node->member_decorations)) {
+        SafeFree(p_node->member_decorations);
+      }
+    }
+
+    // Free functions
+    for (size_t i = 0; i < p_parser->function_count; ++i) {
+      SafeFree(p_parser->functions[i].callees);
+      SafeFree(p_parser->functions[i].callee_ptrs);
+      SafeFree(p_parser->functions[i].accessed_ptrs);
+    }
+
+    // Free access chains
+    for (uint32_t i = 0; i < p_parser->access_chain_count; ++i) {
+      SafeFree(p_parser->access_chains[i].indexes);
+    }
+
+    SafeFree(p_parser->nodes);
+    SafeFree(p_parser->strings);
+    SafeFree(p_parser->source_embedded);
+    SafeFree(p_parser->functions);
+    SafeFree(p_parser->access_chains);
+    p_parser->node_count = 0;
+  }
+}
+
+static SpvReflectResult ParseNodes(SpvReflectPrvParser* p_parser)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+
+  uint32_t* p_spirv = p_parser->spirv_code;
+  uint32_t spirv_word_index = SPIRV_STARTING_WORD_INDEX;
+
+  // Count nodes
+  uint32_t node_count = 0;
+  while (spirv_word_index < p_parser->spirv_word_count) {
+    uint32_t word = p_spirv[spirv_word_index];
+    SpvOp op = (SpvOp)(word & 0xFFFF);
+    uint32_t node_word_count = (word >> 16) & 0xFFFF;
+    if (node_word_count == 0) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_INSTRUCTION;
+    }
+    if (op == SpvOpAccessChain) {
+      ++(p_parser->access_chain_count);
+    }
+    spirv_word_index += node_word_count;
+    ++node_count;
+  }
+
+  if (node_count == 0) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF;
+  }
+
+  // Allocate nodes
+  p_parser->node_count = node_count;
+  p_parser->nodes = (SpvReflectPrvNode*)calloc(p_parser->node_count, sizeof(*(p_parser->nodes)));
+  if (IsNull(p_parser->nodes)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+  // Mark all nodes with an invalid state
+  for (uint32_t i = 0; i < node_count; ++i) {
+    p_parser->nodes[i].op = (SpvOp)INVALID_VALUE;
+    p_parser->nodes[i].storage_class = (SpvStorageClass)INVALID_VALUE;
+    p_parser->nodes[i].decorations.set.value = (uint32_t)INVALID_VALUE;
+    p_parser->nodes[i].decorations.binding.value = (uint32_t)INVALID_VALUE;
+    p_parser->nodes[i].decorations.location.value = (uint32_t)INVALID_VALUE;
+    p_parser->nodes[i].decorations.offset.value = (uint32_t)INVALID_VALUE;
+    p_parser->nodes[i].decorations.uav_counter_buffer.value = (uint32_t)INVALID_VALUE;
+    p_parser->nodes[i].decorations.built_in = (SpvBuiltIn)INVALID_VALUE;
+  }
+  // Mark source file id node
+  p_parser->source_file_id = (uint32_t)INVALID_VALUE;
+  p_parser->source_embedded = NULL;
+
+  // Function node
+  uint32_t function_node = (uint32_t)INVALID_VALUE;
+
+  // Allocate access chain
+  if (p_parser->access_chain_count > 0) {
+    p_parser->access_chains = (SpvReflectPrvAccessChain*)calloc(p_parser->access_chain_count, sizeof(*(p_parser->access_chains)));
+    if (IsNull(p_parser->access_chains)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  // Parse nodes
+  uint32_t node_index = 0;
+  uint32_t access_chain_index = 0;
+  spirv_word_index = SPIRV_STARTING_WORD_INDEX;
+  while (spirv_word_index < p_parser->spirv_word_count) {
+    uint32_t word = p_spirv[spirv_word_index];
+    SpvOp op = (SpvOp)(word & 0xFFFF);
+    uint32_t node_word_count = (word >> 16) & 0xFFFF;
+
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[node_index]);
+    p_node->op = op;
+    p_node->word_offset = spirv_word_index;
+    p_node->word_count = node_word_count;
+
+    switch (p_node->op) {
+      default: break;
+
+      case SpvOpString: {
+        ++(p_parser->string_count);
+      }
+      break;
+
+      case SpvOpSource: {
+        CHECKED_READU32_CAST(p_parser, p_node->word_offset + 1, SpvSourceLanguage, p_parser->source_language);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_parser->source_language_version);
+        if (p_node->word_count >= 4) {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_parser->source_file_id);
+        }
+        if (p_node->word_count >= 5) {
+          const char* p_source = (const char*)(p_parser->spirv_code + p_node->word_offset + 4);
+
+          const size_t source_len = strlen(p_source);
+          char* p_source_temp = (char*)calloc(source_len + 1, sizeof(char*));
+
+          if (IsNull(p_source_temp)) {
+            return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+          }
+
+      #ifdef _WIN32
+          strcpy_s(p_source_temp, source_len + 1, p_source);
+      #else
+          strcpy(p_source_temp, p_source);
+      #endif
+
+          p_parser->source_embedded = p_source_temp;
+        }
+      }
+      break;
+
+      case SpvOpSourceContinued: {
+        const char* p_source = (const char*)(p_parser->spirv_code + p_node->word_offset + 1);
+
+        const size_t source_len = strlen(p_source);
+        const size_t embedded_source_len = strlen(p_parser->source_embedded); 
+        char* p_continued_source = (char*)calloc(source_len + embedded_source_len + 1, sizeof(char*));
+
+        if (IsNull(p_continued_source)) {
+          return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+        }
+
+    #ifdef _WIN32
+        strcpy_s(p_continued_source, embedded_source_len + 1, p_parser->source_embedded);
+        strcat_s(p_continued_source, source_len + 1, p_source);
+    #else
+        strcpy(p_continued_source, p_parser->source_embedded);
+        strcat(p_continued_source, p_source);
+    #endif
+
+        SafeFree(p_parser->source_embedded);
+        p_parser->source_embedded = p_continued_source;
+      }
+      break;
+
+      case SpvOpEntryPoint: {
+        ++(p_parser->entry_point_count);
+      }
+      break;
+
+      case SpvOpCapability: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->capability);
+        ++(p_parser->capability_count);
+      }
+      break;
+
+      case SpvOpName:
+      case SpvOpMemberName:
+      {
+        uint32_t member_offset = (p_node->op == SpvOpMemberName) ? 1 : 0;
+        uint32_t name_start = p_node->word_offset + member_offset + 2;
+        p_node->name = (const char*)(p_parser->spirv_code + name_start);
+      }
+      break;
+
+      case SpvOpTypeStruct:
+      {
+        p_node->member_count = p_node->word_count - 2;
+      } // Fall through
+      case SpvOpTypeVoid:
+      case SpvOpTypeBool:
+      case SpvOpTypeInt:
+      case SpvOpTypeFloat:
+      case SpvOpTypeVector:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeSampler:
+      case SpvOpTypeOpaque:
+      case SpvOpTypeFunction:
+      case SpvOpTypeEvent:
+      case SpvOpTypeDeviceEvent:
+      case SpvOpTypeReserveId:
+      case SpvOpTypeQueue:
+      case SpvOpTypePipe:
+      case SpvOpTypeAccelerationStructureKHR:
+      case SpvOpTypeRayQueryKHR:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypeImage: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->image_traits.sampled_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->image_traits.dim);
+        CHECKED_READU32(p_parser, p_node->word_offset + 4, p_node->image_traits.depth);
+        CHECKED_READU32(p_parser, p_node->word_offset + 5, p_node->image_traits.arrayed);
+        CHECKED_READU32(p_parser, p_node->word_offset + 6, p_node->image_traits.ms);
+        CHECKED_READU32(p_parser, p_node->word_offset + 7, p_node->image_traits.sampled);
+        CHECKED_READU32(p_parser, p_node->word_offset + 8, p_node->image_traits.image_format);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypeSampledImage: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->image_type_id);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypeArray:  {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->array_traits.element_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->array_traits.length_id);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypeRuntimeArray:  {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->array_traits.element_type_id);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypePointer: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->storage_class);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->type_id);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpTypeForwardPointer:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->storage_class);
+        p_node->is_type = true;
+      }
+      break;
+
+      case SpvOpConstantTrue:
+      case SpvOpConstantFalse:
+      case SpvOpConstant:
+      case SpvOpConstantComposite:
+      case SpvOpConstantSampler:
+      case SpvOpConstantNull: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+      }
+      break;
+
+      case SpvOpSpecConstantTrue:
+      case SpvOpSpecConstantFalse:
+      case SpvOpSpecConstant:
+      case SpvOpSpecConstantComposite:
+      case SpvOpSpecConstantOp: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+      }
+      break;
+
+      case SpvOpVariable:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_node->storage_class);
+      }
+      break;
+
+      case SpvOpLoad:
+      {
+        // Only load enough so OpDecorate can reference the node, skip the remaining operands.
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_node->result_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+      }
+      break;
+
+      case SpvOpAccessChain:
+      {
+        SpvReflectPrvAccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
+        CHECKED_READU32(p_parser, p_node->word_offset + 1, p_access_chain->result_type_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_access_chain->result_id);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3, p_access_chain->base_id);
+        //
+        // SPIRV_ACCESS_CHAIN_INDEX_OFFSET (4) is the number of words up until the first index:
+        //   [Node, Result Type Id, Result Id, Base Id, <Indexes>]
+        //
+        p_access_chain->index_count = (node_word_count - SPIRV_ACCESS_CHAIN_INDEX_OFFSET);
+        if (p_access_chain->index_count > 0) {
+          p_access_chain->indexes = (uint32_t*)calloc(p_access_chain->index_count, sizeof(*(p_access_chain->indexes)));
+          if (IsNull( p_access_chain->indexes)) {
+            return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+          }
+          // Parse any index values for access chain
+          for (uint32_t index_index = 0; index_index < p_access_chain->index_count; ++index_index) {
+            // Read index id
+            uint32_t index_id = 0;
+            CHECKED_READU32(p_parser, p_node->word_offset + SPIRV_ACCESS_CHAIN_INDEX_OFFSET + index_index, index_id);
+            // Find OpConstant node that contains index value
+            SpvReflectPrvNode* p_index_value_node = FindNode(p_parser, index_id);
+            if ((p_index_value_node != NULL) && (p_index_value_node->op == SpvOpConstant)) {
+              // Read index value
+              uint32_t index_value = UINT32_MAX;
+              CHECKED_READU32(p_parser, p_index_value_node->word_offset + 3, index_value);
+              assert(index_value != UINT32_MAX);
+              // Write index value to array
+              p_access_chain->indexes[index_index] = index_value;
+            }
+          }
+        }
+        ++access_chain_index;
+      }
+      break;
+
+      case SpvOpFunction:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, p_node->result_id);
+        // Count function definitions, not function declarations.  To determine
+        // the difference, set an in-function variable, and then if an OpLabel
+        // is reached before the end of the function increment the function
+        // count.
+        function_node = node_index;
+      }
+      break;
+
+      case SpvOpLabel:
+      {
+        if (function_node != (uint32_t)INVALID_VALUE) {
+          SpvReflectPrvNode* p_func_node = &(p_parser->nodes[function_node]);
+          CHECKED_READU32(p_parser, p_func_node->word_offset + 2, p_func_node->result_id);
+          ++(p_parser->function_count);
+        }
+      } // Fall through
+
+      case SpvOpFunctionEnd:
+      {
+        function_node = (uint32_t)INVALID_VALUE;
+      }
+      break;
+    }
+
+    if (p_node->is_type) {
+      ++(p_parser->type_count);
+    }
+
+    spirv_word_index += node_word_count;
+    ++node_index;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseStrings(SpvReflectPrvParser* p_parser)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(IsNotNull(p_parser->nodes));
+
+  // Early out
+  if (p_parser->string_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    // Allocate string storage
+    p_parser->strings = (SpvReflectPrvString*)calloc(p_parser->string_count, sizeof(*(p_parser->strings)));  
+
+    uint32_t string_index = 0;
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if (p_node->op != SpvOpString) {
+        continue;
+      }
+
+      // Paranoid check against string count
+      assert(string_index < p_parser->string_count);
+      if (string_index >= p_parser->string_count) {
+        return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+      }
+
+      // Result id
+      SpvReflectPrvString* p_string = &(p_parser->strings[string_index]);
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, p_string->result_id);
+
+      // String
+      uint32_t string_start = p_node->word_offset + 2;
+      p_string->string = (const char*)(p_parser->spirv_code + string_start);
+
+      // Increment string index
+      ++string_index;
+    }
+  }
+  
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseSource(SpvReflectPrvParser* p_parser, SpvReflectShaderModule* p_module)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code)) {
+    // Source file
+    if (IsNotNull(p_parser->strings)) {
+      for (uint32_t i = 0; i < p_parser->string_count; ++i) {
+        SpvReflectPrvString* p_string = &(p_parser->strings[i]);
+        if (p_string->result_id == p_parser->source_file_id) {
+          p_module->source_file = p_string->string;
+          break;
+        }
+      }
+    }
+
+    //Source code
+    if (IsNotNull(p_parser->source_embedded))
+    {
+      const size_t source_len = strlen(p_parser->source_embedded);
+      char* p_source = (char*)calloc(source_len + 1, sizeof(char*));
+
+      if (IsNull(p_source)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+
+    #ifdef _WIN32
+      strcpy_s(p_source, source_len + 1, p_parser->source_embedded);
+    #else
+      strcpy(p_source, p_parser->source_embedded);
+    #endif
+
+      p_module->source_source = p_source;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseFunction(
+  SpvReflectPrvParser*   p_parser, 
+  SpvReflectPrvNode*     p_func_node, 
+  SpvReflectPrvFunction* p_func, 
+  size_t                 first_label_index)
+{
+  p_func->id = p_func_node->result_id;
+
+  p_func->callee_count = 0;
+  p_func->accessed_ptr_count = 0;
+
+  for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op == SpvOpFunctionEnd) {
+      break;
+    }
+    switch (p_node->op) {
+      case SpvOpFunctionCall: {
+        ++(p_func->callee_count);
+      }
+      break;
+      case SpvOpLoad:
+      case SpvOpAccessChain:
+      case SpvOpInBoundsAccessChain:
+      case SpvOpPtrAccessChain:
+      case SpvOpArrayLength:
+      case SpvOpGenericPtrMemSemantics:
+      case SpvOpInBoundsPtrAccessChain:
+      case SpvOpStore:
+      case SpvOpImageTexelPointer:
+      {
+        ++(p_func->accessed_ptr_count);
+      }
+      break;
+      case SpvOpCopyMemory:
+      case SpvOpCopyMemorySized:
+      {
+        p_func->accessed_ptr_count += 2;
+      }
+      break;
+      default: break;
+    }
+  }
+
+  if (p_func->callee_count > 0) {
+    p_func->callees = (uint32_t*)calloc(p_func->callee_count,
+                                        sizeof(*(p_func->callees)));
+    if (IsNull(p_func->callees)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  if (p_func->accessed_ptr_count > 0) {
+    p_func->accessed_ptrs = (uint32_t*)calloc(p_func->accessed_ptr_count,
+                                              sizeof(*(p_func->accessed_ptrs)));
+    if (IsNull(p_func->accessed_ptrs)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  p_func->callee_count = 0;
+  p_func->accessed_ptr_count = 0;
+  for (size_t i = first_label_index; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op == SpvOpFunctionEnd) {
+      break;
+    }
+    switch (p_node->op) {
+      case SpvOpFunctionCall: {
+        CHECKED_READU32(p_parser, p_node->word_offset + 3,
+                        p_func->callees[p_func->callee_count]);
+        (++p_func->callee_count);
+      }
+      break;
+      case SpvOpLoad:
+      case SpvOpAccessChain:
+      case SpvOpInBoundsAccessChain:
+      case SpvOpPtrAccessChain:
+      case SpvOpArrayLength:
+      case SpvOpGenericPtrMemSemantics:
+      case SpvOpInBoundsPtrAccessChain:
+      case SpvOpImageTexelPointer:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 3,
+                        p_func->accessed_ptrs[p_func->accessed_ptr_count]);
+        (++p_func->accessed_ptr_count);
+      }
+      break;
+      case SpvOpStore:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2,
+                        p_func->accessed_ptrs[p_func->accessed_ptr_count]);
+        (++p_func->accessed_ptr_count);
+      }
+      break;
+      case SpvOpCopyMemory:
+      case SpvOpCopyMemorySized:
+      {
+        CHECKED_READU32(p_parser, p_node->word_offset + 2,
+                        p_func->accessed_ptrs[p_func->accessed_ptr_count]);
+        (++p_func->accessed_ptr_count);
+        CHECKED_READU32(p_parser, p_node->word_offset + 3,
+                        p_func->accessed_ptrs[p_func->accessed_ptr_count]);
+        (++p_func->accessed_ptr_count);
+      }
+      break;
+      default: break;
+    }
+  }
+
+  if (p_func->callee_count > 0) {
+    qsort(p_func->callees, p_func->callee_count,
+          sizeof(*(p_func->callees)), SortCompareUint32);
+  }
+  p_func->callee_count = (uint32_t)DedupSortedUint32(p_func->callees,
+                                                     p_func->callee_count);
+
+  if (p_func->accessed_ptr_count > 0) {
+    qsort(p_func->accessed_ptrs, p_func->accessed_ptr_count,
+          sizeof(*(p_func->accessed_ptrs)), SortCompareUint32);
+  }
+  p_func->accessed_ptr_count = (uint32_t)DedupSortedUint32(p_func->accessed_ptrs,
+                                                           p_func->accessed_ptr_count);
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static int SortCompareFunctions(
+  const void* a, 
+  const void* b)
+{
+  const SpvReflectPrvFunction* af = (const SpvReflectPrvFunction*)a;
+  const SpvReflectPrvFunction* bf = (const SpvReflectPrvFunction*)b;
+  return (int)af->id - (int)bf->id;
+}
+
+static SpvReflectResult ParseFunctions(SpvReflectPrvParser* p_parser)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(IsNotNull(p_parser->nodes));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    if (p_parser->function_count == 0) {
+      return SPV_REFLECT_RESULT_SUCCESS;
+    }
+
+    p_parser->functions = (SpvReflectPrvFunction*)calloc(p_parser->function_count,
+                                                         sizeof(*(p_parser->functions)));
+    if (IsNull(p_parser->functions)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+
+    size_t function_index = 0;
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if (p_node->op != SpvOpFunction) {
+        continue;
+      }
+
+      // Skip over function declarations that aren't definitions
+      bool func_definition = false;
+      // Intentionally reuse i to avoid iterating over these nodes more than
+      // once
+      for (; i < p_parser->node_count; ++i) {
+        if (p_parser->nodes[i].op == SpvOpLabel) {
+          func_definition = true;
+          break;
+        }
+        if (p_parser->nodes[i].op == SpvOpFunctionEnd) {
+          break;
+        }
+      }
+      if (!func_definition) {
+        continue;
+      }
+
+      SpvReflectPrvFunction* p_function = &(p_parser->functions[function_index]);
+
+      SpvReflectResult result = ParseFunction(p_parser, p_node, p_function, i);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        return result;
+      }
+
+      ++function_index;
+    }
+
+    qsort(p_parser->functions, p_parser->function_count,
+          sizeof(*(p_parser->functions)), SortCompareFunctions);
+
+    // Once they're sorted, link the functions with pointers to improve graph
+    // traversal efficiency
+    for (size_t i = 0; i < p_parser->function_count; ++i) {
+      SpvReflectPrvFunction* p_func = &(p_parser->functions[i]);
+      if (p_func->callee_count == 0) {
+        continue;
+      }
+      p_func->callee_ptrs = (SpvReflectPrvFunction**)calloc(p_func->callee_count,
+                                                            sizeof(*(p_func->callee_ptrs)));
+      for (size_t j = 0, k = 0; j < p_func->callee_count; ++j) {
+        while (p_parser->functions[k].id != p_func->callees[j]) {
+          ++k;
+          if (k >= p_parser->function_count) {
+            // Invalid called function ID somewhere
+            return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          }
+        }
+        p_func->callee_ptrs[j] = &(p_parser->functions[k]);
+      }
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseMemberCounts(SpvReflectPrvParser* p_parser)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(IsNotNull(p_parser->nodes));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if ((p_node->op != SpvOpMemberName) && (p_node->op != SpvOpMemberDecorate)) {
+        continue;
+      }
+
+      uint32_t target_id = 0;
+      uint32_t member_index = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index);
+      SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
+      // Not all nodes get parsed, so FindNode returning NULL is expected.
+      if (IsNull(p_target_node)) {
+        continue;
+      }
+
+      if (member_index == INVALID_VALUE) {
+        return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED;
+      }
+
+      p_target_node->member_count = Max(p_target_node->member_count, member_index + 1);
+    }
+
+    for (uint32_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if (p_node->member_count == 0) {
+        continue;
+      }
+
+      p_node->member_names = (const char **)calloc(p_node->member_count, sizeof(*(p_node->member_names)));
+      if (IsNull(p_node->member_names)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+
+      p_node->member_decorations = (SpvReflectPrvDecorations*)calloc(p_node->member_count, sizeof(*(p_node->member_decorations)));
+      if (IsNull(p_node->member_decorations)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseNames(SpvReflectPrvParser* p_parser)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->spirv_code));
+  assert(IsNotNull(p_parser->nodes));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    for (size_t i = 0; i < p_parser->node_count; ++i) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+      if ((p_node->op != SpvOpName) && (p_node->op != SpvOpMemberName)) {
+        continue;
+      }
+
+      uint32_t target_id = 0;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
+      SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
+      // Not all nodes get parsed, so FindNode returning NULL is expected.
+      if (IsNull(p_target_node)) {
+        continue;
+      }
+
+      const char** pp_target_name = &(p_target_node->name);
+      if (p_node->op == SpvOpMemberName) {
+        uint32_t member_index = UINT32_MAX;
+        CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index);
+        pp_target_name = &(p_target_node->member_names[member_index]);
+      }
+
+      *pp_target_name = p_node->name;
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDecorations(SpvReflectPrvParser* p_parser)
+{
+  for (uint32_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+
+    if (((uint32_t)p_node->op != (uint32_t)SpvOpDecorate) &&
+        ((uint32_t)p_node->op != (uint32_t)SpvOpMemberDecorate) &&
+        ((uint32_t)p_node->op != (uint32_t)SpvReflectOpDecorateId) &&
+        ((uint32_t)p_node->op != (uint32_t)SpvReflectOpDecorateStringGOOGLE) &&
+        ((uint32_t)p_node->op != (uint32_t)SpvReflectOpMemberDecorateStringGOOGLE))
+    {
+     continue;
+    }
+
+    // Need to adjust the read offset if this is a member decoration
+    uint32_t member_offset = 0;
+    if (p_node->op == SpvOpMemberDecorate) {
+      member_offset = 1;
+    }
+
+    // Get decoration
+    uint32_t decoration = (uint32_t)INVALID_VALUE;
+    CHECKED_READU32(p_parser, p_node->word_offset + member_offset + 2, decoration);
+
+    // Filter out the decoration that do not affect reflection, otherwise
+    // there will be random crashes because the nodes aren't found.
+    bool skip = false;
+    switch (decoration) {
+      default: {
+        skip = true;
+      }
+      break;
+      case SpvDecorationRelaxedPrecision:
+      case SpvDecorationBlock:
+      case SpvDecorationBufferBlock:
+      case SpvDecorationColMajor:
+      case SpvDecorationRowMajor:
+      case SpvDecorationArrayStride:
+      case SpvDecorationMatrixStride:
+      case SpvDecorationBuiltIn:
+      case SpvDecorationNoPerspective:
+      case SpvDecorationFlat:
+      case SpvDecorationNonWritable:
+      case SpvDecorationNonReadable:
+      case SpvDecorationLocation:
+      case SpvDecorationBinding:
+      case SpvDecorationDescriptorSet:
+      case SpvDecorationOffset:
+      case SpvDecorationInputAttachmentIndex:
+      case SpvReflectDecorationHlslCounterBufferGOOGLE:
+      case SpvReflectDecorationHlslSemanticGOOGLE: {
+        skip = false;
+      }
+      break;    
+    }
+    if (skip) {
+      continue;
+    }  
+    
+    // Find target target node 
+    uint32_t target_id = 0;
+    CHECKED_READU32(p_parser, p_node->word_offset + 1, target_id);
+    SpvReflectPrvNode* p_target_node = FindNode(p_parser, target_id);
+    if (IsNull(p_target_node)) {
+      if ((p_node->op == (uint32_t)SpvOpDecorate) && (decoration == SpvDecorationRelaxedPrecision)) {
+        // Many OPs can be decorated that we don't care about. Ignore those.
+        // See https://github.com/KhronosGroup/SPIRV-Reflect/issues/134
+        continue;
+      }
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // Get decorations
+    SpvReflectPrvDecorations* p_target_decorations = &(p_target_node->decorations);
+    // Update pointer if this is a member member decoration
+    if (p_node->op == SpvOpMemberDecorate) {
+      uint32_t member_index = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, member_index);
+      p_target_decorations = &(p_target_node->member_decorations[member_index]);
+    }
+
+    switch (decoration) {
+      default: break;
+
+      case SpvDecorationRelaxedPrecision: {
+        p_target_decorations->is_relaxed_precision = true;
+      }
+      break;
+
+      case SpvDecorationBlock: {
+        p_target_decorations->is_block = true;
+      }
+      break;
+
+      case SpvDecorationBufferBlock: {
+        p_target_decorations->is_buffer_block = true;
+      }
+      break;
+
+      case SpvDecorationColMajor: {
+        p_target_decorations->is_column_major = true;
+      }
+      break;
+
+      case SpvDecorationRowMajor: {
+        p_target_decorations->is_row_major = true;
+      }
+      break;
+
+      case SpvDecorationArrayStride: {
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->array_stride);
+      }
+      break;
+
+      case SpvDecorationMatrixStride: {
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->matrix_stride);
+      }
+      break;
+
+      case SpvDecorationBuiltIn: {
+        p_target_decorations->is_built_in = true;
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        CHECKED_READU32_CAST(p_parser, word_offset, SpvBuiltIn, p_target_decorations->built_in);
+      }
+      break;
+
+      case SpvDecorationNoPerspective: {
+        p_target_decorations->is_noperspective = true;
+      }
+      break;
+
+      case SpvDecorationFlat: {
+        p_target_decorations->is_flat = true;
+      }
+      break;
+
+      case SpvDecorationNonWritable: {
+        p_target_decorations->is_non_writable = true;
+      }
+      break;
+
+      case SpvDecorationNonReadable: {
+        p_target_decorations->is_non_readable = true;
+      }
+      break;
+
+      case SpvDecorationLocation: {
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->location.value);
+        p_target_decorations->location.word_offset = word_offset;
+      }
+      break;
+
+      case SpvDecorationBinding: {
+        uint32_t word_offset = p_node->word_offset + member_offset+ 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->binding.value);
+        p_target_decorations->binding.word_offset = word_offset;
+      }
+      break;
+
+      case SpvDecorationDescriptorSet: {
+        uint32_t word_offset = p_node->word_offset + member_offset+ 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->set.value);
+        p_target_decorations->set.word_offset = word_offset;
+      }
+      break;
+
+      case SpvDecorationOffset: {
+        uint32_t word_offset = p_node->word_offset + member_offset+ 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->offset.value);
+        p_target_decorations->offset.word_offset = word_offset;
+      }
+      break;
+
+      case SpvDecorationInputAttachmentIndex: {
+        uint32_t word_offset = p_node->word_offset + member_offset+ 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->input_attachment_index.value);
+        p_target_decorations->input_attachment_index.word_offset = word_offset;
+      }
+      break;
+
+      case SpvReflectDecorationHlslCounterBufferGOOGLE: {
+        uint32_t word_offset = p_node->word_offset + member_offset+ 3;
+        CHECKED_READU32(p_parser, word_offset, p_target_decorations->uav_counter_buffer.value);
+        p_target_decorations->uav_counter_buffer.word_offset = word_offset;
+      }
+      break;
+
+      case SpvReflectDecorationHlslSemanticGOOGLE: {
+        uint32_t word_offset = p_node->word_offset + member_offset + 3;
+        p_target_decorations->semantic.value = (const char*)(p_parser->spirv_code + word_offset);
+        p_target_decorations->semantic.word_offset = word_offset;
+      }
+      break;
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult EnumerateAllUniforms(
+  SpvReflectShaderModule* p_module,
+  size_t*                 p_uniform_count, 
+  uint32_t**              pp_uniforms
+)
+{
+  *p_uniform_count = p_module->descriptor_binding_count;
+  if (*p_uniform_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+  *pp_uniforms = (uint32_t*)calloc(*p_uniform_count, sizeof(**pp_uniforms));
+
+  if (IsNull(*pp_uniforms)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  for (size_t i = 0; i < *p_uniform_count; ++i) {
+    (*pp_uniforms)[i] = p_module->descriptor_bindings[i].spirv_id;
+  }
+  qsort(*pp_uniforms, *p_uniform_count, sizeof(**pp_uniforms),
+        SortCompareUint32);
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseType(
+  SpvReflectPrvParser*       p_parser, 
+  SpvReflectPrvNode*         p_node, 
+  SpvReflectPrvDecorations*  p_struct_member_decorations, 
+  SpvReflectShaderModule*    p_module, 
+  SpvReflectTypeDescription* p_type
+)
+{
+  SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS;
+
+  if (p_node->member_count > 0) {
+    p_type->member_count = p_node->member_count;
+    p_type->members = (SpvReflectTypeDescription*)calloc(p_type->member_count, sizeof(*(p_type->members)));
+    if (IsNotNull(p_type->members)) {
+      // Mark all members types with an invalid state
+      for (size_t i = 0; i < p_type->members->member_count; ++i) {
+        SpvReflectTypeDescription* p_member_type = &(p_type->members[i]);
+        p_member_type->id = (uint32_t)INVALID_VALUE;
+        p_member_type->op = (SpvOp)INVALID_VALUE;
+        p_member_type->storage_class = (SpvStorageClass)INVALID_VALUE;
+      }
+    }
+    else {
+      result = SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    // Since the parse descends on type information, these will get overwritten
+    // if not guarded against assignment. Only assign if the id is invalid.
+    if (p_type->id == INVALID_VALUE) {
+      p_type->id = p_node->result_id;
+      p_type->op = p_node->op;
+      p_type->decoration_flags = 0;
+    }
+    // Top level types need to pick up decorations from all types below it.
+    // Issue and fix here: https://github.com/chaoticbob/SPIRV-Reflect/issues/64
+    p_type->decoration_flags = ApplyDecorations(&p_node->decorations);
+
+    switch (p_node->op) {
+      default: break;
+      case SpvOpTypeVoid:
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_VOID;
+        break;
+
+      case SpvOpTypeBool:
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_BOOL;
+        break;
+
+      case SpvOpTypeInt: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_INT;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, p_type->traits.numeric.scalar.width);
+        IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.scalar.signedness);
+      }
+      break;
+
+      case SpvOpTypeFloat: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_FLOAT;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, p_type->traits.numeric.scalar.width);
+      }
+      break;
+
+      case SpvOpTypeVector: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_VECTOR;
+        uint32_t component_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, component_type_id);
+        IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.vector.component_count);
+        // Parse component type
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, component_type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          SPV_REFLECT_ASSERT(false);
+        }
+      }
+      break;
+
+      case SpvOpTypeMatrix: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_MATRIX;
+        uint32_t column_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, column_type_id);
+        IF_READU32(result, p_parser, p_node->word_offset + 3, p_type->traits.numeric.matrix.column_count);
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, column_type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          SPV_REFLECT_ASSERT(false);
+        }
+        p_type->traits.numeric.matrix.row_count = p_type->traits.numeric.vector.component_count;
+        p_type->traits.numeric.matrix.stride = p_node->decorations.matrix_stride;
+        // NOTE: Matrix stride is decorated using OpMemberDecoreate - not OpDecoreate.
+        if (IsNotNull(p_struct_member_decorations)) {
+          p_type->traits.numeric.matrix.stride = p_struct_member_decorations->matrix_stride;
+        }
+      }
+      break;
+
+      case SpvOpTypeImage: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE;
+        uint32_t sampled_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, sampled_type_id);
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, sampled_type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        IF_READU32_CAST(result, p_parser, p_node->word_offset + 3, SpvDim, p_type->traits.image.dim);
+        IF_READU32(result, p_parser, p_node->word_offset + 4, p_type->traits.image.depth);
+        IF_READU32(result, p_parser, p_node->word_offset + 5, p_type->traits.image.arrayed);
+        IF_READU32(result, p_parser, p_node->word_offset + 6, p_type->traits.image.ms);
+        IF_READU32(result, p_parser, p_node->word_offset + 7, p_type->traits.image.sampled);
+        IF_READU32_CAST(result, p_parser, p_node->word_offset + 8, SpvImageFormat, p_type->traits.image.image_format);
+      }
+      break;
+
+      case SpvOpTypeSampler: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER;
+      }
+      break;
+
+      case SpvOpTypeSampledImage: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE;
+        uint32_t image_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, image_type_id);
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, image_type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;          
+          SPV_REFLECT_ASSERT(false);
+        }
+      }
+      break;
+
+      case SpvOpTypeArray: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_ARRAY;
+        if (result == SPV_REFLECT_RESULT_SUCCESS) {
+          uint32_t element_type_id = (uint32_t)INVALID_VALUE;
+          uint32_t length_id = (uint32_t)INVALID_VALUE;
+          IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id);
+          IF_READU32(result, p_parser, p_node->word_offset + 3, length_id);
+          // NOTE: Array stride is decorated using OpDecorate instead of
+          //       OpMemberDecorate, even if the array is apart of a struct.
+          p_type->traits.array.stride = p_node->decorations.array_stride;
+          // Get length for current dimension
+          SpvReflectPrvNode* p_length_node = FindNode(p_parser, length_id);
+          if (IsNotNull(p_length_node)) {
+            uint32_t dim_index = p_type->traits.array.dims_count;
+            if (p_length_node->op == SpvOpSpecConstant ||
+                p_length_node->op == SpvOpSpecConstantOp) {
+              p_type->traits.array.dims[dim_index] = 0xFFFFFFFF;
+              p_type->traits.array.spec_constant_op_ids[dim_index] = length_id;
+              p_type->traits.array.dims_count += 1;
+            } else {
+              uint32_t length = 0;
+              IF_READU32(result, p_parser, p_length_node->word_offset + 3, length);
+              if (result == SPV_REFLECT_RESULT_SUCCESS) {
+                // Write the array dim and increment the count and offset
+                p_type->traits.array.dims[dim_index] = length;
+                p_type->traits.array.spec_constant_op_ids[dim_index] = 0xFFFFFFFF;
+                p_type->traits.array.dims_count += 1;
+              } else {
+                result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+                SPV_REFLECT_ASSERT(false);
+              }
+            }
+            // Parse next dimension or element type
+            SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
+            if (IsNotNull(p_next_node)) {
+              result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+            }
+          }
+          else {
+            result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+            SPV_REFLECT_ASSERT(false);
+          }
+        }
+      }
+      break;
+
+      case SpvOpTypeRuntimeArray: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_ARRAY;
+        uint32_t element_type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 2, element_type_id);
+        p_type->traits.array.stride = p_node->decorations.array_stride;
+        uint32_t dim_index = p_type->traits.array.dims_count;
+        p_type->traits.array.dims[dim_index] = 0;
+        p_type->traits.array.spec_constant_op_ids[dim_index] = 0;
+        p_type->traits.array.dims_count += 1;
+        // Parse next dimension or element type
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, element_type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          SPV_REFLECT_ASSERT(false);
+        }
+      }
+      break;
+
+      case SpvOpTypeStruct: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_STRUCT;
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK;
+        uint32_t word_index = 2;
+        uint32_t member_index = 0;
+        for (; word_index < p_node->word_count; ++word_index, ++member_index) {
+          uint32_t member_id = (uint32_t)INVALID_VALUE;
+          IF_READU32(result, p_parser, p_node->word_offset + word_index, member_id);
+          // Find member node
+          SpvReflectPrvNode* p_member_node = FindNode(p_parser, member_id);
+          if (IsNull(p_member_node)) {
+            result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+            SPV_REFLECT_ASSERT(false);
+            break;
+          }
+
+          // Member decorations
+          SpvReflectPrvDecorations* p_member_decorations = &p_node->member_decorations[member_index];
+
+          assert(member_index < p_type->member_count);
+          // Parse member type
+          SpvReflectTypeDescription* p_member_type = &(p_type->members[member_index]);
+          p_member_type->id = member_id;
+          p_member_type->op = p_member_node->op;
+          result = ParseType(p_parser, p_member_node, p_member_decorations, p_module, p_member_type);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            break;
+          }
+          // This looks wrong 
+          //p_member_type->type_name = p_member_node->name;
+          p_member_type->struct_member_name = p_node->member_names[member_index];
+        }
+      }
+      break;
+
+      case SpvOpTypeOpaque: break;
+
+      case SpvOpTypePointer: {
+        IF_READU32_CAST(result, p_parser, p_node->word_offset + 2, SpvStorageClass, p_type->storage_class);
+        uint32_t type_id = (uint32_t)INVALID_VALUE;
+        IF_READU32(result, p_parser, p_node->word_offset + 3, type_id);
+        // Parse type
+        SpvReflectPrvNode* p_next_node = FindNode(p_parser, type_id);
+        if (IsNotNull(p_next_node)) {
+          result = ParseType(p_parser, p_next_node, NULL, p_module, p_type);
+        }
+        else {
+          result = SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+          SPV_REFLECT_ASSERT(false);
+        }
+      }
+      break;
+
+      case SpvOpTypeAccelerationStructureKHR: {
+        p_type->type_flags |= SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE;
+      }
+      break;
+    }
+
+    if (result == SPV_REFLECT_RESULT_SUCCESS) {
+      // Names get assigned on the way down. Guard against names
+      // get overwritten on the way up.
+      if (IsNull(p_type->type_name)) {
+        p_type->type_name = p_node->name;
+      }
+    }
+  }
+
+  return result;
+}
+
+static SpvReflectResult ParseTypes(
+  SpvReflectPrvParser*    p_parser,
+  SpvReflectShaderModule* p_module)
+{
+  if (p_parser->type_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->_internal->type_description_count = p_parser->type_count;
+  p_module->_internal->type_descriptions = (SpvReflectTypeDescription*)calloc(p_module->_internal->type_description_count,
+                                                                              sizeof(*(p_module->_internal->type_descriptions)));
+  if (IsNull(p_module->_internal->type_descriptions)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  // Mark all types with an invalid state
+  for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) {
+    SpvReflectTypeDescription* p_type = &(p_module->_internal->type_descriptions[i]);
+    p_type->id = (uint32_t)INVALID_VALUE;
+    p_type->op = (SpvOp)INVALID_VALUE;
+    p_type->storage_class = (SpvStorageClass)INVALID_VALUE;
+  }
+
+  size_t type_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (! p_node->is_type) {
+      continue;
+    }
+
+    SpvReflectTypeDescription* p_type = &(p_module->_internal->type_descriptions[type_index]);
+    SpvReflectResult result = ParseType(p_parser, p_node, NULL, p_module, p_type);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    ++type_index;
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseCapabilities(
+    SpvReflectPrvParser* p_parser,
+    SpvReflectShaderModule* p_module)
+{
+  if (p_parser->capability_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->capability_count = p_parser->capability_count;
+  p_module->capabilities = (SpvReflectCapability*)calloc(p_module->capability_count,
+                                                         sizeof(*(p_module->capabilities)));
+  if (IsNull(p_module->capabilities)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  // Mark all types with an invalid state
+  for (size_t i = 0; i < p_module->capability_count; ++i) {
+    SpvReflectCapability* p_cap = &(p_module->capabilities[i]);
+    p_cap->value = SpvCapabilityMax;
+    p_cap->word_offset = (uint32_t)INVALID_VALUE;
+  }
+
+  size_t capability_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (SpvOpCapability != p_node->op) {
+      continue;
+    }
+
+    SpvReflectCapability* p_cap = &(p_module->capabilities[capability_index]);
+    p_cap->value = p_node->capability;
+    p_cap->word_offset = p_node->word_offset + 1;
+    ++capability_index;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static int SortCompareDescriptorBinding(const void* a, const void* b)
+{
+  const SpvReflectDescriptorBinding* p_elem_a = (const SpvReflectDescriptorBinding*)a;
+  const SpvReflectDescriptorBinding* p_elem_b = (const SpvReflectDescriptorBinding*)b;
+  int value = (int)(p_elem_a->binding) - (int)(p_elem_b->binding);
+  if (value == 0) {
+    // use spirv-id as a tiebreaker to ensure a stable ordering, as they're guaranteed
+    // unique.
+    assert(p_elem_a->spirv_id != p_elem_b->spirv_id);
+    value = (int)(p_elem_a->spirv_id) - (int)(p_elem_b->spirv_id);
+  }
+  return value;
+}
+
+static SpvReflectResult ParseDescriptorBindings(
+  SpvReflectPrvParser*    p_parser,
+  SpvReflectShaderModule* p_module)
+{
+  p_module->descriptor_binding_count = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) ||
+        ((p_node->storage_class != SpvStorageClassUniform) &&
+         (p_node->storage_class != SpvStorageClassStorageBuffer) &&
+         (p_node->storage_class != SpvStorageClassUniformConstant)))
+    {
+      continue;
+    }
+    if ((p_node->decorations.set.value == INVALID_VALUE) || (p_node->decorations.binding.value == INVALID_VALUE)) {
+      continue;
+    }
+
+    p_module->descriptor_binding_count += 1;
+  }
+
+  if (p_module->descriptor_binding_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->descriptor_bindings = (SpvReflectDescriptorBinding*)calloc(p_module->descriptor_binding_count, sizeof(*(p_module->descriptor_bindings)));
+  if (IsNull(p_module->descriptor_bindings)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  // Mark all types with an invalid state
+  for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]);
+    p_descriptor->binding = (uint32_t)INVALID_VALUE;
+    p_descriptor->input_attachment_index = (uint32_t)INVALID_VALUE;
+    p_descriptor->set = (uint32_t)INVALID_VALUE;
+    p_descriptor->descriptor_type = (SpvReflectDescriptorType)INVALID_VALUE;
+    p_descriptor->uav_counter_id = (uint32_t)INVALID_VALUE;
+  }
+
+  size_t descriptor_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) ||
+        ((p_node->storage_class != SpvStorageClassUniform) &&
+         (p_node->storage_class != SpvStorageClassStorageBuffer) &&
+         (p_node->storage_class != SpvStorageClassUniformConstant)))
+    {
+      continue;
+    }
+    if ((p_node->decorations.set.value == INVALID_VALUE) || (p_node->decorations.binding.value == INVALID_VALUE)) {
+      continue;
+    }
+
+    SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id);
+    if (IsNull(p_type)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // If the type is a pointer, resolve it. We need to retain the storage class
+    // from the pointer so that we can use it to deduce deescriptor types.
+    SpvStorageClass pointer_storage_class = SpvStorageClassMax;
+    if (p_type->op == SpvOpTypePointer) {
+      pointer_storage_class = p_type->storage_class;
+      // Find the type's node
+      SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+      if (IsNull(p_type_node)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+      // Should be the resolved type
+      p_type = FindType(p_module, p_type_node->type_id);
+      if (IsNull(p_type)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+    }
+
+    SpvReflectDescriptorBinding* p_descriptor = &p_module->descriptor_bindings[descriptor_index];
+    p_descriptor->spirv_id = p_node->result_id;
+    p_descriptor->name = p_node->name;
+    p_descriptor->binding = p_node->decorations.binding.value;
+    p_descriptor->input_attachment_index = p_node->decorations.input_attachment_index.value;
+    p_descriptor->set = p_node->decorations.set.value;
+    p_descriptor->count = 1;
+    p_descriptor->uav_counter_id = p_node->decorations.uav_counter_buffer.value;
+    p_descriptor->type_description = p_type;
+    p_descriptor->decoration_flags = ApplyDecorations(&p_node->decorations);
+
+    // If this is in the StorageBuffer storage class, it's for sure a storage
+    // buffer descriptor. We need to handle this case earlier because in SPIR-V
+    // there are two ways to indicate a storage buffer:
+    // 1) Uniform storage class + BufferBlock decoration, or
+    // 2) StorageBuffer storage class + Buffer decoration.
+    // The 1) way is deprecated since SPIR-V v1.3. But the Buffer decoration is
+    // also used together with Uniform storage class to mean uniform buffer..
+    // We'll handle the pre-v1.3 cases in ParseDescriptorType().
+    if (pointer_storage_class == SpvStorageClassStorageBuffer) {
+      p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+    }
+
+    // Copy image traits
+    if ((p_type->type_flags & SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK) == SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE) {
+      memcpy(&p_descriptor->image, &p_type->traits.image, sizeof(p_descriptor->image));
+    }
+
+    // This is a workaround for: https://github.com/KhronosGroup/glslang/issues/1096
+    {
+      const uint32_t resource_mask = SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE | SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE;
+      if ((p_type->type_flags & resource_mask) == resource_mask) {
+        memcpy(&p_descriptor->image, &p_type->traits.image, sizeof(p_descriptor->image));
+      }
+    }
+
+    // Copy array traits
+    if (p_type->traits.array.dims_count > 0) {
+      p_descriptor->array.dims_count = p_type->traits.array.dims_count;
+      for (uint32_t dim_index = 0; dim_index < p_type->traits.array.dims_count; ++dim_index) {
+        uint32_t dim_value = p_type->traits.array.dims[dim_index];
+        p_descriptor->array.dims[dim_index] = dim_value;
+        p_descriptor->count *= dim_value;
+      }
+    }
+
+    // Count
+
+
+    p_descriptor->word_offset.binding = p_node->decorations.binding.word_offset;
+    p_descriptor->word_offset.set = p_node->decorations.set.word_offset;
+
+    ++descriptor_index;
+  }
+
+  if (p_module->descriptor_binding_count > 0) {
+    qsort(p_module->descriptor_bindings,
+          p_module->descriptor_binding_count,
+          sizeof(*(p_module->descriptor_bindings)),
+          SortCompareDescriptorBinding);
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorType(SpvReflectShaderModule* p_module)
+{
+  if (p_module->descriptor_binding_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]);
+    SpvReflectTypeDescription* p_type = p_descriptor->type_description;
+
+    if ((int)p_descriptor->descriptor_type == (int)INVALID_VALUE) {
+      switch (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK) {
+        default: assert(false && "unknown type flag"); break;
+
+        case SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE: {
+          if (p_descriptor->image.dim == SpvDimBuffer) {
+            switch (p_descriptor->image.sampled) {
+              default: assert(false && "unknown texel buffer sampled value"); break;
+              case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; break;
+              case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; break;
+            }
+          }
+          else if(p_descriptor->image.dim == SpvDimSubpassData) {
+            p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
+          }
+          else {
+            switch (p_descriptor->image.sampled) {
+              default: assert(false && "unknown image sampled value"); break;
+              case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE; break;
+              case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE; break;
+            }
+          }
+        }
+        break;
+
+        case SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER: {
+          p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER;
+        }
+        break;
+
+        case (SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE | SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE): {
+          // This is a workaround for: https://github.com/KhronosGroup/glslang/issues/1096
+          if (p_descriptor->image.dim == SpvDimBuffer) {
+            switch (p_descriptor->image.sampled) {
+              default: assert(false && "unknown texel buffer sampled value"); break;
+              case IMAGE_SAMPLED: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; break;
+              case IMAGE_STORAGE: p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; break;
+            }
+          }
+          else {
+            p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+          }
+        }
+        break;
+
+        case SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK: {
+          if (p_type->decoration_flags & SPV_REFLECT_DECORATION_BLOCK) {
+            p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+          }
+          else if (p_type->decoration_flags & SPV_REFLECT_DECORATION_BUFFER_BLOCK) {
+            p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+          }
+          else {
+            assert(false && "unknown struct");
+          }
+        }
+        break;
+
+        case SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE: {
+          p_descriptor->descriptor_type = SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
+        }
+        break;
+      }
+    }
+
+    switch (p_descriptor->descriptor_type) {
+      case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER                    : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SAMPLER; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER     : p_descriptor->resource_type = (SpvReflectResourceType)(SPV_REFLECT_RESOURCE_FLAG_SAMPLER | SPV_REFLECT_RESOURCE_FLAG_SRV); break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE              : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE              : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER       : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER       : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER             : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_CBV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC     : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_CBV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER             : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC     : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_UAV; break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT           : break;
+      case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR : p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV; break;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseUAVCounterBindings(SpvReflectShaderModule* p_module)
+{
+  char name[MAX_NODE_NAME_LENGTH];
+  const char* k_count_tag = "@count";
+
+  for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]);
+
+    if (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
+      continue;
+    }
+
+    SpvReflectDescriptorBinding* p_counter_descriptor = NULL;
+    // Use UAV counter buffer id if present...
+    if (p_descriptor->uav_counter_id != UINT32_MAX) {
+      for (uint32_t counter_descriptor_index = 0; counter_descriptor_index < p_module->descriptor_binding_count; ++counter_descriptor_index) {
+        SpvReflectDescriptorBinding* p_test_counter_descriptor = &(p_module->descriptor_bindings[counter_descriptor_index]);
+        if (p_test_counter_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
+          continue;
+        }
+        if (p_descriptor->uav_counter_id == p_test_counter_descriptor->spirv_id) {
+          p_counter_descriptor = p_test_counter_descriptor;
+          break;
+        }
+      }
+    }
+    // ...otherwise use old @count convention.
+    else {
+      const size_t descriptor_name_length = p_descriptor->name? strlen(p_descriptor->name): 0;
+
+      memset(name, 0, MAX_NODE_NAME_LENGTH);    
+      memcpy(name, p_descriptor->name, descriptor_name_length);
+#if defined(_WIN32)
+      strcat_s(name, MAX_NODE_NAME_LENGTH, k_count_tag);
+#else
+      strcat(name, k_count_tag);
+#endif
+
+      for (uint32_t counter_descriptor_index = 0; counter_descriptor_index < p_module->descriptor_binding_count; ++counter_descriptor_index) {
+        SpvReflectDescriptorBinding* p_test_counter_descriptor = &(p_module->descriptor_bindings[counter_descriptor_index]);
+        if (p_test_counter_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
+          continue;
+        }
+        if (p_test_counter_descriptor->name && strcmp(name, p_test_counter_descriptor->name) == 0) {
+          p_counter_descriptor = p_test_counter_descriptor;
+          break;
+        }
+      }
+    }
+
+    if (p_counter_descriptor != NULL) {
+      p_descriptor->uav_counter_binding = p_counter_descriptor;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorBlockVariable(
+  SpvReflectPrvParser*        p_parser, 
+  SpvReflectShaderModule*     p_module, 
+  SpvReflectTypeDescription*  p_type, 
+  SpvReflectBlockVariable*    p_var
+)
+{
+  bool has_non_writable = false;
+
+  if (IsNotNull(p_type->members) && (p_type->member_count > 0)) {
+    p_var->member_count = p_type->member_count;
+    p_var->members = (SpvReflectBlockVariable*)calloc(p_var->member_count, sizeof(*p_var->members));
+    if (IsNull(p_var->members)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+
+    SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+    if (IsNull(p_type_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // Resolve to element type if current type is array or run time array
+    while (p_type_node->op == SpvOpTypeArray || p_type_node->op == SpvOpTypeRuntimeArray) {
+      if (p_type_node->op == SpvOpTypeArray) {
+        p_type_node = FindNode(p_parser, p_type_node->array_traits.element_type_id);
+      }
+      else {
+        // Element type description
+        SpvReflectTypeDescription* p_type_temp = FindType(p_module, p_type_node->array_traits.element_type_id);
+        if (IsNull(p_type_temp)) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        // Element type node
+        p_type_node = FindNode(p_parser, p_type_temp->id);
+      }
+      if (IsNull(p_type_node)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+    }
+    
+    // Parse members
+    for (uint32_t member_index = 0; member_index < p_type->member_count; ++member_index) {
+      SpvReflectTypeDescription* p_member_type = &p_type->members[member_index];
+      SpvReflectBlockVariable* p_member_var = &p_var->members[member_index];
+      bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT;
+      if (is_struct) {
+        SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_member_type, p_member_var);
+        if (result != SPV_REFLECT_RESULT_SUCCESS) {
+          return result;
+        }
+      }
+
+      p_member_var->name = p_type_node->member_names[member_index];
+      p_member_var->offset = p_type_node->member_decorations[member_index].offset.value;
+      p_member_var->decoration_flags = ApplyDecorations(&p_type_node->member_decorations[member_index]);
+      p_member_var->flags |= SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+      if (!has_non_writable && (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) {
+        has_non_writable = true;
+      }
+      ApplyNumericTraits(p_member_type, &p_member_var->numeric);
+      if (p_member_type->op == SpvOpTypeArray) {
+        ApplyArrayTraits(p_member_type, &p_member_var->array);
+      }
+
+      p_member_var->type_description = p_member_type;
+    }
+  }
+
+  p_var->name = p_type->type_name;
+  p_var->type_description = p_type;
+  if (has_non_writable) {
+    p_var->decoration_flags |= SPV_REFLECT_DECORATION_NON_WRITABLE;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorBlockVariableSizes(
+  SpvReflectPrvParser*     p_parser, 
+  SpvReflectShaderModule*  p_module, 
+  bool                     is_parent_root, 
+  bool                     is_parent_aos, 
+  bool                     is_parent_rta, 
+  SpvReflectBlockVariable* p_var
+)
+{
+  if (p_var->member_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  // Absolute offsets
+  for (uint32_t member_index = 0; member_index < p_var->member_count; ++member_index) {
+    SpvReflectBlockVariable* p_member_var = &p_var->members[member_index];
+    if (is_parent_root) {
+      p_member_var->absolute_offset = p_member_var->offset;
+    }
+    else {
+      p_member_var->absolute_offset = is_parent_aos ? 0 : p_member_var->offset + p_var->absolute_offset;
+    }
+  }
+
+  // Size
+  for (uint32_t member_index = 0; member_index < p_var->member_count; ++member_index) {
+    SpvReflectBlockVariable* p_member_var = &p_var->members[member_index];
+    SpvReflectTypeDescription* p_member_type = p_member_var->type_description;
+
+    switch (p_member_type->op) {
+      case SpvOpTypeBool: {
+        p_member_var->size = SPIRV_WORD_SIZE;
+      }
+      break;
+
+      case SpvOpTypeInt:
+      case SpvOpTypeFloat: {
+        p_member_var->size = p_member_type->traits.numeric.scalar.width / SPIRV_BYTE_WIDTH;
+      }
+      break;
+
+      case SpvOpTypeVector: {
+        uint32_t size = p_member_type->traits.numeric.vector.component_count *
+                        (p_member_type->traits.numeric.scalar.width / SPIRV_BYTE_WIDTH);
+        p_member_var->size = size;
+      }
+      break;
+
+      case SpvOpTypeMatrix: {
+        if (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_COLUMN_MAJOR) {
+          p_member_var->size = p_member_var->numeric.matrix.column_count * p_member_var->numeric.matrix.stride;
+        }
+        else if (p_member_var->decoration_flags & SPV_REFLECT_DECORATION_ROW_MAJOR) {
+          p_member_var->size = p_member_var->numeric.matrix.row_count * p_member_var->numeric.matrix.stride;
+        }
+      }
+      break;
+
+      case SpvOpTypeArray: {
+        // If array of structs, parse members first...
+        bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT;
+        if (is_struct) {
+          SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, true, is_parent_rta, p_member_var);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            return result;
+          }
+        }
+        // ...then array
+        uint32_t element_count = (p_member_var->array.dims_count > 0 ? 1 : 0);
+        for (uint32_t i = 0; i < p_member_var->array.dims_count; ++i) {
+          element_count *= p_member_var->array.dims[i];
+        }
+        p_member_var->size = element_count * p_member_var->array.stride;
+      }
+      break;
+
+      case SpvOpTypeRuntimeArray: {
+        bool is_struct = (p_member_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) == SPV_REFLECT_TYPE_FLAG_STRUCT;
+        if (is_struct) {
+          SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, true, true, p_member_var);
+          if (result != SPV_REFLECT_RESULT_SUCCESS) {
+            return result;
+          }
+        }
+      }
+      break;
+
+      case SpvOpTypeStruct: {
+        SpvReflectResult result = ParseDescriptorBlockVariableSizes(p_parser, p_module, false, is_parent_aos, is_parent_rta, p_member_var);
+        if (result != SPV_REFLECT_RESULT_SUCCESS) {
+          return result;
+        }
+      }
+      break;
+
+      default:
+        break;
+    }
+  }
+
+  // Parse padded size using offset difference for all member except for the last entry...
+  for (uint32_t member_index = 0; member_index < (p_var->member_count - 1); ++member_index) {
+    SpvReflectBlockVariable* p_member_var = &p_var->members[member_index];
+    SpvReflectBlockVariable* p_next_member_var = &p_var->members[member_index + 1];
+    p_member_var->padded_size = p_next_member_var->offset - p_member_var->offset;
+    if (p_member_var->size > p_member_var->padded_size) {
+      p_member_var->size = p_member_var->padded_size;
+    }
+    if (is_parent_rta) {
+      p_member_var->padded_size = p_member_var->size;
+    }
+  }
+  // ...last entry just gets rounded up to near multiple of SPIRV_DATA_ALIGNMENT, which is 16 and
+  // subtract the offset.
+  if (p_var->member_count > 0) {
+    SpvReflectBlockVariable* p_member_var = &p_var->members[p_var->member_count - 1];
+    p_member_var->padded_size = RoundUp(p_member_var->offset  + p_member_var->size, SPIRV_DATA_ALIGNMENT) - p_member_var->offset;
+    if (p_member_var->size > p_member_var->padded_size) {
+      p_member_var->size = p_member_var->padded_size;
+    }
+    if (is_parent_rta) {
+      p_member_var->padded_size = p_member_var->size;
+    }
+  }
+
+  // @TODO validate this with assertion
+  p_var->size = p_var->members[p_var->member_count - 1].offset +
+                p_var->members[p_var->member_count - 1].padded_size;
+  p_var->padded_size = p_var->size;
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static void MarkSelfAndAllMemberVarsAsUsed(SpvReflectBlockVariable* p_var)
+{
+  // Clear the current variable's USED flag
+  p_var->flags &= ~SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+
+  SpvOp op_type = p_var->type_description->op;
+  switch (op_type) {
+    default: break;
+
+    case SpvOpTypeArray: {
+    }
+    break;
+
+    case SpvOpTypeStruct: {
+      for (uint32_t i = 0; i < p_var->member_count; ++i) {
+        SpvReflectBlockVariable* p_member_var = &p_var->members[i];
+        MarkSelfAndAllMemberVarsAsUsed(p_member_var);
+      }
+    }
+    break;
+  }
+}
+
+static SpvReflectResult ParseDescriptorBlockVariableUsage(
+  SpvReflectPrvParser*      p_parser,
+  SpvReflectShaderModule*   p_module,
+  SpvReflectPrvAccessChain* p_access_chain,
+  uint32_t                  index_index,
+  SpvOp                     override_op_type,
+  SpvReflectBlockVariable*  p_var
+)
+{
+  (void)p_parser;
+  (void)p_access_chain;
+  (void)p_var;
+
+  // Clear the current variable's UNUSED flag
+  p_var->flags &= ~SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+  
+  // Parsing arrays requires overriding the op type for
+  // for the lowest dim's element type.
+  SpvOp op_type = p_var->type_description->op;
+  if (override_op_type != (SpvOp)INVALID_VALUE) {
+    op_type = override_op_type;
+  }
+
+  switch (op_type) {
+    default: break;
+
+    case SpvOpTypeArray: {
+      // Parse through array's type hierarchy to find the actual/non-array element type
+      SpvReflectTypeDescription* p_type = p_var->type_description;
+      while ((p_type->op == SpvOpTypeArray) && (index_index < p_access_chain->index_count)) {
+        // Find the array element type id
+        SpvReflectPrvNode* p_node = FindNode(p_parser, p_type->id);
+        if (p_node == NULL) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        uint32_t element_type_id = p_node->array_traits.element_type_id;
+        // Get the array element type
+        p_type = FindType(p_module, element_type_id);
+        if (p_type == NULL) {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+        }
+        // Next access chain index
+        index_index += 1;
+      }
+      
+      // Only continue parsing if there's remaining indices in the access
+      // chain. If the end of the access chain has been reach then all
+      // remaining variables (including those in struct hierarchies)
+      // are considered USED.
+      //
+      // See: https://github.com/KhronosGroup/SPIRV-Reflect/issues/78
+      //  
+      if (index_index < p_access_chain->index_count) {
+        // Parse current var again with a type override and advanced index index
+        SpvReflectResult result = ParseDescriptorBlockVariableUsage(
+          p_parser,
+          p_module,
+          p_access_chain,
+          index_index,
+          p_type->op,
+          p_var);
+        if (result != SPV_REFLECT_RESULT_SUCCESS) {
+          return result;
+        }
+      }
+      else {
+        // Clear UNUSED flag for remaining variables
+        MarkSelfAndAllMemberVarsAsUsed(p_var);
+      }
+    }
+    break;
+
+    case SpvOpTypeStruct: {
+      assert(p_var->member_count > 0);
+      if (p_var->member_count == 0) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_BLOCK_DATA;
+      }
+      // Get member variable at the access's chain current index
+      uint32_t index = p_access_chain->indexes[index_index];
+      if (index >= p_var->member_count) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_BLOCK_MEMBER_REFERENCE;
+      }
+      SpvReflectBlockVariable* p_member_var = &p_var->members[index];
+
+      // Next access chain index
+      index_index += 1;
+
+      // Only continue parsing if there's remaining indices in the access
+      // chain. If the end of the access chain has been reach then all
+      // remaining variables (including those in struct hierarchies)
+      // are considered USED.
+      //
+      // See: https://github.com/KhronosGroup/SPIRV-Reflect/issues/78
+      //
+      if (index_index < p_access_chain->index_count) {
+         SpvReflectResult result = ParseDescriptorBlockVariableUsage(
+           p_parser,
+           p_module,
+           p_access_chain,
+           index_index,
+           (SpvOp)INVALID_VALUE,
+           p_member_var);
+         if (result != SPV_REFLECT_RESULT_SUCCESS) {
+           return result;
+         }
+      }
+      else {
+        // Clear UNUSED flag for remaining variables
+        MarkSelfAndAllMemberVarsAsUsed(p_member_var);
+      }
+      //SpvReflectBlockVariable* p_member_var = &p_var->members[index];
+      //if (index_index < p_access_chain->index_count) {
+      //  SpvReflectResult result = ParseDescriptorBlockVariableUsage(
+      //    p_parser,
+      //    p_module,
+      //    p_access_chain,
+      //    index_index + 1,
+      //    (SpvOp)INVALID_VALUE,
+      //    p_member_var);
+      //  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      //    return result;
+      //  }
+      //}
+    }
+    break;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorBlocks(
+  SpvReflectPrvParser*    p_parser, 
+  SpvReflectShaderModule* p_module)
+{
+  if (p_module->descriptor_binding_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]);
+    SpvReflectTypeDescription* p_type = p_descriptor->type_description;
+    if ((p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) &&
+        (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) )
+    {
+      continue;
+    }
+
+    // Mark UNUSED
+    p_descriptor->block.flags |= SPV_REFLECT_VARIABLE_FLAGS_UNUSED;
+    // Parse descriptor block 
+    SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_type, &p_descriptor->block);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+
+    for (uint32_t access_chain_index = 0; access_chain_index < p_parser->access_chain_count; ++access_chain_index) {
+      SpvReflectPrvAccessChain* p_access_chain = &(p_parser->access_chains[access_chain_index]);
+      // Skip any access chains that aren't touching this descriptor block
+      if (p_descriptor->spirv_id != p_access_chain->base_id) {
+        continue;
+      }
+      result = ParseDescriptorBlockVariableUsage(
+        p_parser,
+        p_module,
+        p_access_chain,
+        0,
+        (SpvOp)INVALID_VALUE,
+        &p_descriptor->block);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        return result;
+      }
+    }
+    
+    p_descriptor->block.name = p_descriptor->name;
+
+    bool is_parent_rta = (p_descriptor->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+    result = ParseDescriptorBlockVariableSizes(p_parser, p_module, true, false, is_parent_rta, &p_descriptor->block);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+
+    if (is_parent_rta) {
+      p_descriptor->block.size = 0;
+      p_descriptor->block.padded_size = 0;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseFormat(
+  const SpvReflectTypeDescription*  p_type,
+  SpvReflectFormat*                 p_format
+)
+{
+  SpvReflectResult result = SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR;
+  bool signedness = (p_type->traits.numeric.scalar.signedness != 0);
+  uint32_t bit_width = p_type->traits.numeric.scalar.width;
+  if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_VECTOR) {
+    uint32_t component_count = p_type->traits.numeric.vector.component_count;
+    if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) {
+      switch (bit_width) {
+        case 32: {
+          switch (component_count) {
+            case 2: *p_format = SPV_REFLECT_FORMAT_R32G32_SFLOAT; break;
+            case 3: *p_format = SPV_REFLECT_FORMAT_R32G32B32_SFLOAT; break;
+            case 4: *p_format = SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT; break;
+          }
+        }
+        break;
+        
+        case 64: {
+          switch (component_count) {
+            case 2: *p_format = SPV_REFLECT_FORMAT_R64G64_SFLOAT; break;
+            case 3: *p_format = SPV_REFLECT_FORMAT_R64G64B64_SFLOAT; break;
+            case 4: *p_format = SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT; break;
+          }
+        }
+      }
+      result = SPV_REFLECT_RESULT_SUCCESS;
+    }
+    else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) {
+      switch (bit_width) {
+        case 32: {
+          switch (component_count) {
+            case 2: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32_SINT : SPV_REFLECT_FORMAT_R32G32_UINT; break;
+            case 3: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32B32_SINT : SPV_REFLECT_FORMAT_R32G32B32_UINT; break;
+            case 4: *p_format = signedness ? SPV_REFLECT_FORMAT_R32G32B32A32_SINT : SPV_REFLECT_FORMAT_R32G32B32A32_UINT; break;
+          }
+        }
+        break;
+        
+        case 64: {
+          switch (component_count) {
+            case 2: *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64_SINT : SPV_REFLECT_FORMAT_R64G64_UINT; break;
+            case 3: *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64B64_SINT : SPV_REFLECT_FORMAT_R64G64B64_UINT; break;
+            case 4: *p_format = signedness ? SPV_REFLECT_FORMAT_R64G64B64A64_SINT : SPV_REFLECT_FORMAT_R64G64B64A64_UINT; break;
+          }
+        }
+      }
+      result = SPV_REFLECT_RESULT_SUCCESS;
+    }
+  }
+  else if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_FLOAT) {
+    switch(bit_width) {
+      case 32:
+        *p_format = SPV_REFLECT_FORMAT_R32_SFLOAT;
+        break;
+      case 64:
+        *p_format = SPV_REFLECT_FORMAT_R64_SFLOAT;
+        break;
+    }
+    result = SPV_REFLECT_RESULT_SUCCESS;
+  }
+  else if (p_type->type_flags & (SPV_REFLECT_TYPE_FLAG_INT | SPV_REFLECT_TYPE_FLAG_BOOL)) {
+    switch(bit_width) {
+      case 32:
+        *p_format = signedness ? SPV_REFLECT_FORMAT_R32_SINT : SPV_REFLECT_FORMAT_R32_UINT; break;
+        break;
+      case 64:
+        *p_format = signedness ? SPV_REFLECT_FORMAT_R64_SINT : SPV_REFLECT_FORMAT_R64_UINT; break;
+    }        
+    result = SPV_REFLECT_RESULT_SUCCESS;
+  }
+  else if (p_type->type_flags & SPV_REFLECT_TYPE_FLAG_STRUCT) {
+    *p_format = SPV_REFLECT_FORMAT_UNDEFINED;
+    result = SPV_REFLECT_RESULT_SUCCESS;
+  }
+  return result;
+}
+
+static SpvReflectResult ParseInterfaceVariable(
+  SpvReflectPrvParser*            p_parser,
+  const SpvReflectPrvDecorations* p_var_node_decorations,
+  const SpvReflectPrvDecorations* p_type_node_decorations,
+  SpvReflectShaderModule*         p_module,
+  SpvReflectTypeDescription*      p_type,
+  SpvReflectInterfaceVariable*    p_var,
+  bool*                           p_has_built_in
+)
+{
+  SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+  if (IsNull(p_type_node)) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+  }
+
+  if (p_type->member_count > 0) {
+    p_var->member_count = p_type->member_count;
+    p_var->members = (SpvReflectInterfaceVariable*)calloc(p_var->member_count, sizeof(*p_var->members));
+    if (IsNull(p_var->members)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+
+    for (uint32_t member_index = 0; member_index < p_type_node->member_count; ++member_index) {
+      SpvReflectPrvDecorations* p_member_decorations = &p_type_node->member_decorations[member_index];
+      SpvReflectTypeDescription* p_member_type = &p_type->members[member_index];
+      SpvReflectInterfaceVariable* p_member_var = &p_var->members[member_index];
+      SpvReflectResult result = ParseInterfaceVariable(p_parser, NULL, p_member_decorations, p_module, p_member_type, p_member_var, p_has_built_in);
+      if (result != SPV_REFLECT_RESULT_SUCCESS) {
+        SPV_REFLECT_ASSERT(false);
+        return result;
+      }
+    }
+  }
+
+  p_var->name = p_type_node->name;
+  p_var->decoration_flags = ApplyDecorations(p_type_node_decorations);
+  if (p_var_node_decorations != NULL) {
+    p_var->decoration_flags |= ApplyDecorations(p_var_node_decorations);
+  }
+  p_var->built_in = p_type_node_decorations->built_in;
+  ApplyNumericTraits(p_type, &p_var->numeric);
+  if (p_type->op == SpvOpTypeArray) {
+    ApplyArrayTraits(p_type, &p_var->array);
+  }
+
+  p_var->type_description = p_type;
+
+  *p_has_built_in |= p_type_node_decorations->is_built_in;
+
+  // Only parse format for interface variables that are input or output
+  if ((p_var->storage_class == SpvStorageClassInput) || (p_var->storage_class == SpvStorageClassOutput)) {
+    SpvReflectResult result = ParseFormat(p_var->type_description, &p_var->format);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      SPV_REFLECT_ASSERT(false);
+      return result;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseInterfaceVariables(
+  SpvReflectPrvParser*    p_parser,
+  SpvReflectShaderModule* p_module,
+  SpvReflectEntryPoint*   p_entry,
+  uint32_t                interface_variable_count,
+  uint32_t*               p_interface_variable_ids
+)
+{
+  if (interface_variable_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_entry->interface_variable_count = interface_variable_count;
+  p_entry->input_variable_count = 0;
+  p_entry->output_variable_count = 0;  
+  for (size_t i = 0; i < interface_variable_count; ++i) {
+    uint32_t var_result_id = *(p_interface_variable_ids + i);
+    SpvReflectPrvNode* p_node = FindNode(p_parser, var_result_id);
+    if (IsNull(p_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+ 
+    if (p_node->storage_class == SpvStorageClassInput) {
+      p_entry->input_variable_count += 1;
+    }
+    else if (p_node->storage_class == SpvStorageClassOutput) {
+      p_entry->output_variable_count += 1;
+    }
+ }
+ 
+ if (p_entry->input_variable_count > 0) {
+   p_entry->input_variables = (SpvReflectInterfaceVariable**)calloc(p_entry->input_variable_count, sizeof(*(p_entry->input_variables)));
+   if (IsNull(p_entry->input_variables)) {
+     return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+   }
+ }
+ 
+ if (p_entry->output_variable_count > 0) {
+   p_entry->output_variables = (SpvReflectInterfaceVariable**)calloc(p_entry->output_variable_count, sizeof(*(p_entry->output_variables)));
+   if (IsNull(p_entry->output_variables)) {
+     return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+   }
+ }
+
+  if (p_entry->interface_variable_count > 0) {
+    p_entry->interface_variables = (SpvReflectInterfaceVariable*)calloc(p_entry->interface_variable_count, sizeof(*(p_entry->interface_variables)));
+    if (IsNull(p_entry->interface_variables)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  size_t input_index = 0;
+  size_t output_index = 0;
+  for (size_t i = 0; i < interface_variable_count; ++i) {
+    uint32_t var_result_id = *(p_interface_variable_ids + i);
+    SpvReflectPrvNode* p_node = FindNode(p_parser, var_result_id);
+    if (IsNull(p_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+
+    SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id);
+    if (IsNull(p_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // If the type is a pointer, resolve it
+    if (p_type->op == SpvOpTypePointer) {
+      // Find the type's node
+      SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+      if (IsNull(p_type_node)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+      // Should be the resolved type
+      p_type = FindType(p_module, p_type_node->type_id);
+      if (IsNull(p_type)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+    }
+
+    SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+    if (IsNull(p_type_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+
+    SpvReflectInterfaceVariable* p_var = &(p_entry->interface_variables[i]);
+    p_var->storage_class  = p_node->storage_class;
+
+    bool has_built_in = p_node->decorations.is_built_in;
+    SpvReflectResult result = ParseInterfaceVariable(
+      p_parser,
+      &p_node->decorations,
+      &p_type_node->decorations,
+      p_module,
+      p_type,
+      p_var,
+      &has_built_in);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      SPV_REFLECT_ASSERT(false);
+      return result;
+    }
+
+    // Input and output variables
+    if (p_var->storage_class == SpvStorageClassInput) {
+      p_entry->input_variables[input_index] = p_var;
+       ++input_index;
+    }
+    else if (p_node->storage_class == SpvStorageClassOutput) {
+      p_entry->output_variables[output_index] = p_var;
+      ++output_index;
+    }
+
+    // SPIR-V result id
+    p_var->spirv_id = p_node->result_id;
+    // Name
+    p_var->name = p_node->name;
+    // Semantic
+    p_var->semantic = p_node->decorations.semantic.value;
+
+    // Decorate with built-in if any member is built-in
+    if (has_built_in) {
+      p_var->decoration_flags |= SPV_REFLECT_DECORATION_BUILT_IN;
+    }
+
+    // Location is decorated on OpVariable node, not the type node.
+    p_var->location = p_node->decorations.location.value;
+    p_var->word_offset.location = p_node->decorations.location.word_offset;
+
+    // Built in
+    if (p_node->decorations.is_built_in) {
+      p_var->built_in = p_node->decorations.built_in;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult EnumerateAllPushConstants(
+  SpvReflectShaderModule* p_module,
+  size_t*                 p_push_constant_count,
+  uint32_t**              p_push_constants
+)
+{
+  *p_push_constant_count = p_module->push_constant_block_count;
+  if (*p_push_constant_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+  *p_push_constants = (uint32_t*)calloc(*p_push_constant_count, sizeof(**p_push_constants));
+
+  if (IsNull(*p_push_constants)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  for (size_t i = 0; i < *p_push_constant_count; ++i) {
+    (*p_push_constants)[i] = p_module->push_constant_blocks[i].spirv_id;
+  }
+  qsort(*p_push_constants, *p_push_constant_count, sizeof(**p_push_constants),
+        SortCompareUint32);
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult TraverseCallGraph(
+  SpvReflectPrvParser*   p_parser,
+  SpvReflectPrvFunction* p_func,
+  size_t*                p_func_count,
+  uint32_t*              p_func_ids,
+  uint32_t               depth
+)
+{
+  if (depth > p_parser->function_count) {
+    // Vulkan does not permit recursion (Vulkan spec Appendix A):
+    //   "Recursion: The static function-call graph for an entry point must not
+    //    contain cycles."
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_RECURSION;
+  }
+  if (IsNotNull(p_func_ids)) {
+    p_func_ids[(*p_func_count)++] = p_func->id;
+  } else {
+    ++*p_func_count;
+  }
+  for (size_t i = 0; i < p_func->callee_count; ++i) {
+    SpvReflectResult result = TraverseCallGraph(
+        p_parser, p_func->callee_ptrs[i], p_func_count, p_func_ids, depth + 1);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseStaticallyUsedResources(
+  SpvReflectPrvParser*    p_parser,
+  SpvReflectShaderModule* p_module,
+  SpvReflectEntryPoint*   p_entry,
+  size_t                  uniform_count,
+  uint32_t*               uniforms,
+  size_t                  push_constant_count,
+  uint32_t*               push_constants
+)
+{
+  // Find function with the right id
+  SpvReflectPrvFunction* p_func = NULL;
+  for (size_t i = 0; i < p_parser->function_count; ++i) {
+    if (p_parser->functions[i].id == p_entry->id) {
+      p_func = &(p_parser->functions[i]);
+      break;
+    }
+  }
+  if (p_func == NULL) {
+    return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+  }
+
+  size_t called_function_count = 0;
+  SpvReflectResult result = TraverseCallGraph(
+    p_parser,
+    p_func,
+    &called_function_count,
+    NULL,
+    0);
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    return result;
+  }
+
+  uint32_t* p_called_functions = NULL;
+  if (called_function_count > 0) {
+    p_called_functions = (uint32_t*)calloc(called_function_count, sizeof(*p_called_functions));
+    if (IsNull(p_called_functions)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+
+  called_function_count = 0;
+  result = TraverseCallGraph(
+    p_parser,
+    p_func,
+    &called_function_count,
+    p_called_functions,
+    0);
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    return result;
+  }
+
+  if (called_function_count > 0) {
+    qsort(
+      p_called_functions, 
+      called_function_count, 
+      sizeof(*p_called_functions),
+      SortCompareUint32);
+  }
+  called_function_count = DedupSortedUint32(p_called_functions, called_function_count);
+
+  uint32_t used_variable_count = 0;
+  for (size_t i = 0, j = 0; i < called_function_count; ++i) {
+    // No need to bounds check j because a missing ID issue would have been
+    // found during TraverseCallGraph
+    while (p_parser->functions[j].id != p_called_functions[i]) {
+      ++j;
+    }
+    used_variable_count += p_parser->functions[j].accessed_ptr_count;
+  }
+  uint32_t* used_variables = NULL;
+  if (used_variable_count > 0) {
+    used_variables = (uint32_t*)calloc(used_variable_count,
+                                       sizeof(*used_variables));
+    if (IsNull(used_variables)) {
+      SafeFree(p_called_functions);
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+  }
+  used_variable_count = 0;
+  for (size_t i = 0, j = 0; i < called_function_count; ++i) {
+    while (p_parser->functions[j].id != p_called_functions[i]) {
+      ++j;
+    }
+
+    memcpy(&used_variables[used_variable_count],
+           p_parser->functions[j].accessed_ptrs,
+           p_parser->functions[j].accessed_ptr_count * sizeof(*used_variables));
+    used_variable_count += p_parser->functions[j].accessed_ptr_count;
+  }
+  SafeFree(p_called_functions);
+
+  if (used_variable_count > 0) {
+    qsort(used_variables, used_variable_count, sizeof(*used_variables),
+          SortCompareUint32);
+  }
+  used_variable_count = (uint32_t)DedupSortedUint32(used_variables, 
+                                                    used_variable_count);
+
+  // Do set intersection to find the used uniform and push constants
+  size_t used_uniform_count = 0;
+  //
+  SpvReflectResult result0 = IntersectSortedUint32(
+    used_variables,
+    used_variable_count,
+    uniforms, 
+    uniform_count,
+    &p_entry->used_uniforms,
+    &used_uniform_count);
+
+  size_t used_push_constant_count = 0;
+  //
+  SpvReflectResult result1 = IntersectSortedUint32(
+    used_variables, 
+    used_variable_count,
+    push_constants, 
+    push_constant_count,
+    &p_entry->used_push_constants,
+    &used_push_constant_count);
+
+  for (uint32_t j = 0; j < p_module->descriptor_binding_count; ++j) {
+    SpvReflectDescriptorBinding* p_binding = &p_module->descriptor_bindings[j];
+    bool found = SearchSortedUint32(
+      used_variables,
+      used_variable_count,
+      p_binding->spirv_id);
+    if (found) {
+      p_binding->accessed = 1;
+    }
+  }
+
+  SafeFree(used_variables);
+  if (result0 != SPV_REFLECT_RESULT_SUCCESS) {
+    return result0;
+  }
+  if (result1 != SPV_REFLECT_RESULT_SUCCESS) {
+    return result1;
+  }
+
+  p_entry->used_uniform_count = (uint32_t)used_uniform_count;
+  p_entry->used_push_constant_count = (uint32_t)used_push_constant_count;
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseEntryPoints(
+  SpvReflectPrvParser*    p_parser, 
+  SpvReflectShaderModule* p_module)
+{
+  if (p_parser->entry_point_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->entry_point_count = p_parser->entry_point_count;
+  p_module->entry_points = (SpvReflectEntryPoint*)calloc(p_module->entry_point_count,
+                                                         sizeof(*(p_module->entry_points)));
+  if (IsNull(p_module->entry_points)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  SpvReflectResult result;
+  size_t uniform_count = 0;
+  uint32_t* uniforms = NULL;
+  if ((result = EnumerateAllUniforms(p_module, &uniform_count, &uniforms)) !=
+       SPV_REFLECT_RESULT_SUCCESS) {
+    return result;
+  }
+  size_t push_constant_count = 0;
+  uint32_t* push_constants = NULL;
+  if ((result = EnumerateAllPushConstants(p_module, &push_constant_count, &push_constants)) !=
+       SPV_REFLECT_RESULT_SUCCESS) {
+    return result;
+  }
+
+  size_t entry_point_index = 0;
+  for (size_t i = 0; entry_point_index < p_parser->entry_point_count && i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if (p_node->op != SpvOpEntryPoint) {
+      continue;
+    }
+
+    SpvReflectEntryPoint* p_entry_point = &(p_module->entry_points[entry_point_index]);
+    CHECKED_READU32_CAST(p_parser, p_node->word_offset + 1, SpvExecutionModel, p_entry_point->spirv_execution_model);
+    CHECKED_READU32(p_parser, p_node->word_offset + 2, p_entry_point->id);
+
+    switch (p_entry_point->spirv_execution_model) {
+      default: break;
+      case SpvExecutionModelVertex                 : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_VERTEX_BIT; break;
+      case SpvExecutionModelTessellationControl    : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break;
+      case SpvExecutionModelTessellationEvaluation : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break;
+      case SpvExecutionModelGeometry               : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT; break;
+      case SpvExecutionModelFragment               : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT; break;
+      case SpvExecutionModelGLCompute              : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT; break;
+      case SpvExecutionModelTaskNV                 : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV; break;
+      case SpvExecutionModelMeshNV                 : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV; break;
+      case SpvExecutionModelRayGenerationKHR       : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_RAYGEN_BIT_KHR; break;
+      case SpvExecutionModelIntersectionKHR        : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_INTERSECTION_BIT_KHR; break;
+      case SpvExecutionModelAnyHitKHR              : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_ANY_HIT_BIT_KHR; break;
+      case SpvExecutionModelClosestHitKHR          : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; break;
+      case SpvExecutionModelMissKHR                : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_MISS_BIT_KHR; break;
+      case SpvExecutionModelCallableKHR            : p_entry_point->shader_stage = SPV_REFLECT_SHADER_STAGE_CALLABLE_BIT_KHR; break;
+    }
+
+    ++entry_point_index;
+
+    // Name length is required to calculate next operand
+    uint32_t name_start_word_offset = 3;
+    uint32_t name_length_with_terminator = 0;
+    result = ReadStr(p_parser, p_node->word_offset + name_start_word_offset, 0, p_node->word_count, &name_length_with_terminator, NULL);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    p_entry_point->name = (const char*)(p_parser->spirv_code + p_node->word_offset + name_start_word_offset);
+
+    uint32_t name_word_count = RoundUp(name_length_with_terminator, SPIRV_WORD_SIZE) / SPIRV_WORD_SIZE;
+    uint32_t interface_variable_count = (p_node->word_count - (name_start_word_offset + name_word_count));
+    uint32_t* p_interface_variables = NULL;
+    if (interface_variable_count > 0) {
+      p_interface_variables = (uint32_t*)calloc(interface_variable_count, sizeof(*(p_interface_variables)));
+      if (IsNull(p_interface_variables)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+    }
+
+    for (uint32_t var_index = 0; var_index < interface_variable_count; ++var_index) {
+      uint32_t var_result_id = (uint32_t)INVALID_VALUE;
+      uint32_t offset = name_start_word_offset + name_word_count + var_index;
+      CHECKED_READU32(p_parser, p_node->word_offset + offset, var_result_id);
+      p_interface_variables[var_index] = var_result_id;
+    }
+
+    result = ParseInterfaceVariables(
+      p_parser,
+      p_module,
+      p_entry_point,
+      interface_variable_count,
+      p_interface_variables);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    SafeFree(p_interface_variables);
+
+    result = ParseStaticallyUsedResources(
+      p_parser,
+      p_module,
+      p_entry_point,
+      uniform_count,
+      uniforms,
+      push_constant_count,
+      push_constants);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+  }
+
+  SafeFree(uniforms);
+  SafeFree(push_constants);
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseExecutionModes(
+  SpvReflectPrvParser*    p_parser,
+  SpvReflectShaderModule* p_module)
+{
+  assert(IsNotNull(p_parser));
+  assert(IsNotNull(p_parser->nodes));
+  assert(IsNotNull(p_module));
+
+  if (IsNotNull(p_parser) && IsNotNull(p_parser->spirv_code) && IsNotNull(p_parser->nodes)) {
+    for (size_t node_idx = 0; node_idx < p_parser->node_count; ++node_idx) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[node_idx]);
+      if (p_node->op != SpvOpExecutionMode) {
+        continue;
+      }
+
+      // Read entry point id
+      uint32_t entry_point_id = 0;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, entry_point_id);
+
+      // Find entry point
+      SpvReflectEntryPoint* p_entry_point = NULL;
+      for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+        if (p_module->entry_points[entry_point_idx].id == entry_point_id) {
+          p_entry_point = &p_module->entry_points[entry_point_idx];
+          break;
+        }
+      }
+      // Bail if entry point is null
+      if (IsNull(p_entry_point)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ENTRY_POINT;
+      }
+
+      // Read execution mode
+      uint32_t execution_mode = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, execution_mode);
+
+      // Parse execution mode
+      switch (execution_mode) {
+        default: {
+          return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_EXECUTION_MODE;
+        }
+        break;
+
+        case SpvExecutionModeInvocations: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->invocations);
+        }
+        break;
+
+        case SpvExecutionModeSpacingEqual:
+        case SpvExecutionModeSpacingFractionalEven:
+        case SpvExecutionModeSpacingFractionalOdd:
+        case SpvExecutionModeVertexOrderCw:
+        case SpvExecutionModeVertexOrderCcw:
+        case SpvExecutionModePixelCenterInteger:
+        case SpvExecutionModeOriginUpperLeft:
+        case SpvExecutionModeOriginLowerLeft:
+        case SpvExecutionModeEarlyFragmentTests:
+        case SpvExecutionModePointMode:
+        case SpvExecutionModeXfb:
+        case SpvExecutionModeDepthReplacing:
+        case SpvExecutionModeDepthGreater:
+        case SpvExecutionModeDepthLess:
+        case SpvExecutionModeDepthUnchanged:
+          break;
+
+        case SpvExecutionModeLocalSize: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->local_size.x);
+          CHECKED_READU32(p_parser, p_node->word_offset + 4, p_entry_point->local_size.y);
+          CHECKED_READU32(p_parser, p_node->word_offset + 5, p_entry_point->local_size.z);
+        }
+        break;
+
+        case SpvExecutionModeLocalSizeHint:
+        case SpvExecutionModeInputPoints:
+        case SpvExecutionModeInputLines:
+        case SpvExecutionModeInputLinesAdjacency:
+        case SpvExecutionModeTriangles:
+        case SpvExecutionModeInputTrianglesAdjacency:
+        case SpvExecutionModeQuads:
+        case SpvExecutionModeIsolines:
+        case SpvExecutionModeOutputVertices: {
+          CHECKED_READU32(p_parser, p_node->word_offset + 3, p_entry_point->output_vertices);
+        }
+        break;
+
+        case SpvExecutionModeOutputPoints:
+        case SpvExecutionModeOutputLineStrip:
+        case SpvExecutionModeOutputTriangleStrip:
+        case SpvExecutionModeVecTypeHint:
+        case SpvExecutionModeContractionOff:
+        case SpvExecutionModeInitializer:
+        case SpvExecutionModeFinalizer:
+        case SpvExecutionModeSubgroupSize:
+        case SpvExecutionModeSubgroupsPerWorkgroup:
+        case SpvExecutionModeSubgroupsPerWorkgroupId:
+        case SpvExecutionModeLocalSizeId:
+        case SpvExecutionModeLocalSizeHintId:
+        case SpvExecutionModePostDepthCoverage:
+        case SpvExecutionModeDenormPreserve:
+        case SpvExecutionModeDenormFlushToZero:
+        case SpvExecutionModeSignedZeroInfNanPreserve:
+        case SpvExecutionModeRoundingModeRTE:
+        case SpvExecutionModeRoundingModeRTZ:
+        case SpvExecutionModeStencilRefReplacingEXT:
+        case SpvExecutionModeOutputLinesNV:
+        case SpvExecutionModeOutputPrimitivesNV:
+        case SpvExecutionModeOutputTrianglesNV:
+          break;
+      }
+      p_entry_point->execution_mode_count++;
+    }
+    uint32_t* indices = (uint32_t*)calloc(p_module->entry_point_count, sizeof(indices));
+    if (IsNull(indices)) {
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+    for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+      SpvReflectEntryPoint* p_entry_point = &p_module->entry_points[entry_point_idx];
+      p_entry_point->execution_modes =
+          (SpvExecutionMode*)calloc(p_entry_point->execution_mode_count, sizeof(*p_entry_point->execution_modes));
+      if (IsNull(p_entry_point->execution_modes)) {
+        SafeFree(indices);
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+    }
+
+    for (size_t node_idx = 0; node_idx < p_parser->node_count; ++node_idx) {
+      SpvReflectPrvNode* p_node = &(p_parser->nodes[node_idx]);
+      if (p_node->op != SpvOpExecutionMode) {
+        continue;
+      }
+
+      // Read entry point id
+      uint32_t entry_point_id = 0;
+      CHECKED_READU32(p_parser, p_node->word_offset + 1, entry_point_id);
+
+      // Find entry point
+      SpvReflectEntryPoint* p_entry_point = NULL;
+      uint32_t* idx = NULL;
+      for (size_t entry_point_idx = 0; entry_point_idx < p_module->entry_point_count; ++entry_point_idx) {
+        if (p_module->entry_points[entry_point_idx].id == entry_point_id) {
+          p_entry_point = &p_module->entry_points[entry_point_idx];
+          idx = &indices[entry_point_idx];
+          break;
+        }
+      }
+
+      // Read execution mode
+      uint32_t execution_mode = (uint32_t)INVALID_VALUE;
+      CHECKED_READU32(p_parser, p_node->word_offset + 2, execution_mode);
+      p_entry_point->execution_modes[(*idx)++] = (SpvExecutionMode)execution_mode;
+    }
+    SafeFree(indices);
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParsePushConstantBlocks(
+  SpvReflectPrvParser*    p_parser, 
+  SpvReflectShaderModule* p_module)
+{
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) {
+      continue;
+    }
+
+    p_module->push_constant_block_count += 1;
+  }
+
+  if (p_module->push_constant_block_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  p_module->push_constant_blocks = (SpvReflectBlockVariable*)calloc(p_module->push_constant_block_count, sizeof(*p_module->push_constant_blocks));
+  if (IsNull(p_module->push_constant_blocks)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+
+  uint32_t push_constant_index = 0;
+  for (size_t i = 0; i < p_parser->node_count; ++i) {
+    SpvReflectPrvNode* p_node = &(p_parser->nodes[i]);
+    if ((p_node->op != SpvOpVariable) || (p_node->storage_class != SpvStorageClassPushConstant)) {
+      continue;
+    }
+
+    SpvReflectTypeDescription* p_type = FindType(p_module, p_node->type_id);
+    if (IsNull(p_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+    // If the type is a pointer, resolve it
+    if (p_type->op == SpvOpTypePointer) {
+      // Find the type's node
+      SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+      if (IsNull(p_type_node)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+      // Should be the resolved type
+      p_type = FindType(p_module, p_type_node->type_id);
+      if (IsNull(p_type)) {
+        return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+      }
+    }
+
+    SpvReflectPrvNode* p_type_node = FindNode(p_parser, p_type->id);
+    if (IsNull(p_type_node)) {
+      return SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE;
+    }
+
+    SpvReflectBlockVariable* p_push_constant = &p_module->push_constant_blocks[push_constant_index];
+    p_push_constant->spirv_id = p_node->result_id;
+    SpvReflectResult result = ParseDescriptorBlockVariable(p_parser, p_module, p_type, p_push_constant);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+    result = ParseDescriptorBlockVariableSizes(p_parser, p_module, true, false, false, p_push_constant);
+    if (result != SPV_REFLECT_RESULT_SUCCESS) {
+      return result;
+    }
+
+    ++push_constant_index;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static int SortCompareDescriptorSet(const void* a, const void* b)
+{
+  const SpvReflectDescriptorSet* p_elem_a = (const SpvReflectDescriptorSet*)a;
+  const SpvReflectDescriptorSet* p_elem_b = (const SpvReflectDescriptorSet*)b;
+  int value = (int)(p_elem_a->set) - (int)(p_elem_b->set);
+  // We should never see duplicate descriptor set numbers in a shader; if so, a tiebreaker
+  // would be needed here.
+  assert(value != 0);
+  return value;
+}
+
+static SpvReflectResult ParseEntrypointDescriptorSets(SpvReflectShaderModule* p_module) {
+  // Update the entry point's sets
+  for (uint32_t i = 0; i < p_module->entry_point_count; ++i) {
+    SpvReflectEntryPoint* p_entry = &p_module->entry_points[i];
+    for (uint32_t j = 0; j < p_entry->descriptor_set_count; ++j) {
+      SafeFree(p_entry->descriptor_sets[j].bindings);
+    }
+    SafeFree(p_entry->descriptor_sets);
+    p_entry->descriptor_set_count = 0;
+    for (uint32_t j = 0; j < p_module->descriptor_set_count; ++j) {
+      const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j];
+      for (uint32_t k = 0; k < p_set->binding_count; ++k) {
+        bool found = SearchSortedUint32(
+          p_entry->used_uniforms,
+          p_entry->used_uniform_count,
+          p_set->bindings[k]->spirv_id);
+        if (found) {
+          ++p_entry->descriptor_set_count;
+          break;
+        }
+      }
+    }
+
+    p_entry->descriptor_sets = NULL;
+    if (p_entry->descriptor_set_count > 0) {
+      p_entry->descriptor_sets = (SpvReflectDescriptorSet*)calloc(p_entry->descriptor_set_count,
+                                                                  sizeof(*p_entry->descriptor_sets));
+      if (IsNull(p_entry->descriptor_sets)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+    }
+    p_entry->descriptor_set_count = 0;
+    for (uint32_t j = 0; j < p_module->descriptor_set_count; ++j) {
+      const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j];
+      uint32_t count = 0;
+      for (uint32_t k = 0; k < p_set->binding_count; ++k) {
+        bool found = SearchSortedUint32(
+          p_entry->used_uniforms,
+          p_entry->used_uniform_count,
+          p_set->bindings[k]->spirv_id);
+        if (found) {
+          ++count;
+        }
+      }
+      if (count == 0) {
+        continue;
+      }
+      SpvReflectDescriptorSet* p_entry_set = &p_entry->descriptor_sets[
+          p_entry->descriptor_set_count++];
+      p_entry_set->set = p_set->set;
+      p_entry_set->bindings = (SpvReflectDescriptorBinding**)calloc(count,
+                                                                    sizeof(*p_entry_set->bindings));
+      if (IsNull(p_entry_set->bindings)) {
+        return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+      }
+      for (uint32_t k = 0; k < p_set->binding_count; ++k) {
+        bool found = SearchSortedUint32(
+          p_entry->used_uniforms,
+          p_entry->used_uniform_count,
+          p_set->bindings[k]->spirv_id);
+        if (found) {
+          p_entry_set->bindings[p_entry_set->binding_count++] = p_set->bindings[k];
+        }
+      }
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult ParseDescriptorSets(SpvReflectShaderModule* p_module)
+{
+  // Count the descriptors in each set
+  for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[i]);
+
+    // Look for a target set using the descriptor's set number
+    SpvReflectDescriptorSet* p_target_set = NULL;
+    for (uint32_t j = 0; j < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++j) {
+      SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j];
+      if (p_set->set == p_descriptor->set) {
+        p_target_set = p_set;
+        break;
+      }
+    }
+
+    // If a target set isn't found, find the first available one.
+    if (IsNull(p_target_set)) {
+      for (uint32_t j = 0; j < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++j) {
+        SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[j];
+        if (p_set->set == (uint32_t)INVALID_VALUE) {
+          p_target_set = p_set;
+          p_target_set->set = p_descriptor->set;
+          break;
+        }
+      }
+    }
+
+    if (IsNull(p_target_set)) {
+      return SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR;
+    }
+
+    p_target_set->binding_count += 1;
+  }
+
+  // Count the descriptor sets
+  for (uint32_t i = 0; i < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++i) {
+    const SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i];
+    if (p_set->set != (uint32_t)INVALID_VALUE) {
+      p_module->descriptor_set_count += 1;
+    }
+  }
+
+  // Sort the descriptor sets based on numbers
+  if (p_module->descriptor_set_count > 0) {
+    qsort(p_module->descriptor_sets,
+          p_module->descriptor_set_count,
+          sizeof(*(p_module->descriptor_sets)),
+          SortCompareDescriptorSet);
+  }
+
+  // Build descriptor pointer array
+  for (uint32_t i = 0; i <p_module->descriptor_set_count; ++i) {
+    SpvReflectDescriptorSet* p_set = &(p_module->descriptor_sets[i]);
+    p_set->bindings = (SpvReflectDescriptorBinding **)calloc(p_set->binding_count, sizeof(*(p_set->bindings)));
+
+    uint32_t descriptor_index = 0;
+    for (uint32_t j = 0; j < p_module->descriptor_binding_count; ++j) {
+      SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[j]);
+      if (p_descriptor->set == p_set->set) {
+        assert(descriptor_index < p_set->binding_count);
+        p_set->bindings[descriptor_index] = p_descriptor;
+        ++descriptor_index;
+      }
+    }
+  }
+
+  return ParseEntrypointDescriptorSets(p_module);
+}
+
+static SpvReflectResult DisambiguateStorageBufferSrvUav(SpvReflectShaderModule* p_module)
+{
+  if (p_module->descriptor_binding_count == 0) {
+    return SPV_REFLECT_RESULT_SUCCESS;
+  }
+
+  for (uint32_t descriptor_index = 0; descriptor_index < p_module->descriptor_binding_count; ++descriptor_index) {
+    SpvReflectDescriptorBinding* p_descriptor = &(p_module->descriptor_bindings[descriptor_index]);
+    // Skip everything that isn't a STORAGE_BUFFER descriptor
+    if (p_descriptor->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER) {
+      continue;
+    }
+
+    //
+    // Vulkan doesn't disambiguate between SRVs and UAVs so they
+    // come back as STORAGE_BUFFER. The block parsing process will
+    // mark a block as non-writable should any member of the block
+    // or its descendants are non-writable.
+    //
+    if (p_descriptor->block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) {
+      p_descriptor->resource_type = SPV_REFLECT_RESOURCE_FLAG_SRV;
+    }
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+static SpvReflectResult SynchronizeDescriptorSets(SpvReflectShaderModule* p_module)
+{
+  // Free and reset all descriptor set numbers
+  for (uint32_t i = 0; i < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++i) {
+    SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i];
+    SafeFree(p_set->bindings);
+    p_set->binding_count = 0;
+    p_set->set = (uint32_t)INVALID_VALUE;
+  }
+  // Set descriptor set count to zero
+  p_module->descriptor_set_count = 0;
+
+  SpvReflectResult result = ParseDescriptorSets(p_module);
+  return result;
+}
+
+static SpvReflectResult CreateShaderModule(
+  uint32_t                 flags,
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+)
+{
+  // Initialize all module fields to zero
+  memset(p_module, 0, sizeof(*p_module));
+
+  // Allocate module internals
+#ifdef __cplusplus
+  p_module->_internal = (SpvReflectShaderModule::Internal*)calloc(1, sizeof(*(p_module->_internal)));
+#else
+  p_module->_internal = calloc(1, sizeof(*(p_module->_internal)));
+#endif
+  if (IsNull(p_module->_internal)) {
+    return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+  }
+  // Copy flags
+  p_module->_internal->module_flags = flags;
+  // Figure out if we need to copy the SPIR-V code or not
+  if (flags & SPV_REFLECT_MODULE_FLAG_NO_COPY) {
+    // Set internal size and pointer to args passed in
+    p_module->_internal->spirv_size = size;
+#if defined(__cplusplus)
+    p_module->_internal->spirv_code = const_cast<uint32_t*>(static_cast<const uint32_t*>(p_code)); // cast that const away
+#else
+    p_module->_internal->spirv_code = (void*)p_code; // cast that const away
+#endif
+    p_module->_internal->spirv_word_count = (uint32_t)(size / SPIRV_WORD_SIZE);
+  }
+  else {
+    // Allocate SPIR-V code storage
+    p_module->_internal->spirv_size = size;
+    p_module->_internal->spirv_code = (uint32_t*)calloc(1, p_module->_internal->spirv_size);
+    p_module->_internal->spirv_word_count = (uint32_t)(size / SPIRV_WORD_SIZE);
+    if (IsNull(p_module->_internal->spirv_code)) {
+      SafeFree(p_module->_internal);
+      return SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED;
+    }
+    // Copy SPIR-V to code storage
+    memcpy(p_module->_internal->spirv_code, p_code, size);
+  }
+
+  SpvReflectPrvParser parser = { 0 };
+  SpvReflectResult result = CreateParser(p_module->_internal->spirv_size,
+                                         p_module->_internal->spirv_code,
+                                         &parser);
+
+  // Generator
+  {
+    const uint32_t* p_ptr = (const uint32_t*)p_module->_internal->spirv_code;
+    p_module->generator = (SpvReflectGenerator)((*(p_ptr + 2) & 0xFFFF0000) >> 16);
+  }
+
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseNodes(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseStrings(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseSource(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseFunctions(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseMemberCounts(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseNames(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDecorations(&parser);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+
+  // Start of reflection data parsing
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    p_module->source_language = parser.source_language;
+    p_module->source_language_version = parser.source_language_version;
+
+    // Zero out descriptor set data
+    p_module->descriptor_set_count = 0;
+    memset(p_module->descriptor_sets, 0, SPV_REFLECT_MAX_DESCRIPTOR_SETS * sizeof(*p_module->descriptor_sets));
+    // Initialize descriptor set numbers
+    for (uint32_t set_number = 0; set_number < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++set_number) {
+      p_module->descriptor_sets[set_number].set = (uint32_t)INVALID_VALUE;
+    }
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseTypes(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorBindings(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorType(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseUAVCounterBindings(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseDescriptorBlocks(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParsePushConstantBlocks(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseEntryPoints(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseCapabilities(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS && p_module->entry_point_count > 0) {
+    SpvReflectEntryPoint* p_entry = &(p_module->entry_points[0]);
+    p_module->entry_point_name = p_entry->name;
+    p_module->entry_point_id = p_entry->id;
+    p_module->spirv_execution_model = p_entry->spirv_execution_model;
+    p_module->shader_stage = p_entry->shader_stage;
+    p_module->input_variable_count = p_entry->input_variable_count;
+    p_module->input_variables = p_entry->input_variables;
+    p_module->output_variable_count = p_entry->output_variable_count;
+    p_module->output_variables = p_entry->output_variables;
+    p_module->interface_variable_count = p_entry->interface_variable_count;
+    p_module->interface_variables = p_entry->interface_variables;
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = DisambiguateStorageBufferSrvUav(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = SynchronizeDescriptorSets(p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+  if (result == SPV_REFLECT_RESULT_SUCCESS) {
+    result = ParseExecutionModes(&parser, p_module);
+    SPV_REFLECT_ASSERT(result == SPV_REFLECT_RESULT_SUCCESS);
+  }
+
+  // Destroy module if parse was not successful
+  if (result != SPV_REFLECT_RESULT_SUCCESS) {
+    spvReflectDestroyShaderModule(p_module);
+  }
+
+  DestroyParser(&parser);
+
+  return result;
+}
+
+SpvReflectResult spvReflectCreateShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+)
+{
+    return CreateShaderModule(0, size, p_code, p_module);
+}
+
+SpvReflectResult spvReflectCreateShaderModule2(
+  uint32_t                 flags,
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+)
+{
+  return CreateShaderModule(flags, size, p_code, p_module);
+}
+
+SpvReflectResult spvReflectGetShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+)
+{
+    return spvReflectCreateShaderModule(size, p_code, p_module);
+}
+
+static void SafeFreeTypes(SpvReflectTypeDescription* p_type)
+{
+  if (IsNull(p_type)) {
+    return;
+  }
+
+  if (IsNotNull(p_type->members)) {
+    for (size_t i = 0; i < p_type->member_count; ++i) {
+      SpvReflectTypeDescription* p_member = &p_type->members[i];
+      SafeFreeTypes(p_member);
+    }
+
+    SafeFree(p_type->members);
+    p_type->members = NULL;
+  }
+}
+
+static void SafeFreeBlockVariables(SpvReflectBlockVariable* p_block)
+{
+  if (IsNull(p_block)) {
+    return;
+  }
+
+  if (IsNotNull(p_block->members)) {
+    for (size_t i = 0; i < p_block->member_count; ++i) {
+      SpvReflectBlockVariable* p_member = &p_block->members[i];
+      SafeFreeBlockVariables(p_member);
+    }
+
+    SafeFree(p_block->members);
+    p_block->members = NULL;
+  }
+}
+
+static void SafeFreeInterfaceVariable(SpvReflectInterfaceVariable* p_interface)
+{
+  if (IsNull(p_interface)) {
+    return;
+  }
+
+  if (IsNotNull(p_interface->members)) {
+    for (size_t i = 0; i < p_interface->member_count; ++i) {
+      SpvReflectInterfaceVariable* p_member = &p_interface->members[i];
+      SafeFreeInterfaceVariable(p_member);
+    }
+
+    SafeFree(p_interface->members);
+    p_interface->members = NULL;
+  }
+}
+
+void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module)
+{
+  if (IsNull(p_module->_internal)) {
+    return;
+  }
+
+  SafeFree(p_module->source_source);
+
+  // Descriptor set bindings
+  for (size_t i = 0; i < p_module->descriptor_set_count; ++i) {
+    SpvReflectDescriptorSet* p_set = &p_module->descriptor_sets[i];
+    free(p_set->bindings);
+  }
+
+  // Descriptor binding blocks
+  for (size_t i = 0; i < p_module->descriptor_binding_count; ++i) {
+    SpvReflectDescriptorBinding* p_descriptor = &p_module->descriptor_bindings[i];
+    SafeFreeBlockVariables(&p_descriptor->block);
+  }
+  SafeFree(p_module->descriptor_bindings);
+
+  // Entry points
+  for (size_t i = 0; i < p_module->entry_point_count; ++i) {
+    SpvReflectEntryPoint* p_entry = &p_module->entry_points[i];
+    for (size_t j = 0; j < p_entry->interface_variable_count; j++) {
+      SafeFreeInterfaceVariable(&p_entry->interface_variables[j]);
+    }
+    for (uint32_t j = 0; j < p_entry->descriptor_set_count; ++j) {
+      SafeFree(p_entry->descriptor_sets[j].bindings);
+    }
+    SafeFree(p_entry->descriptor_sets);
+    SafeFree(p_entry->input_variables);
+    SafeFree(p_entry->output_variables);
+    SafeFree(p_entry->interface_variables);
+    SafeFree(p_entry->used_uniforms);
+    SafeFree(p_entry->used_push_constants);
+    SafeFree(p_entry->execution_modes);
+  }
+  SafeFree(p_module->capabilities);
+  SafeFree(p_module->entry_points);
+
+  // Push constants
+  for (size_t i = 0; i < p_module->push_constant_block_count; ++i) {
+    SafeFreeBlockVariables(&p_module->push_constant_blocks[i]);
+  }
+  SafeFree(p_module->push_constant_blocks);
+
+  // Type infos
+  for (size_t i = 0; i < p_module->_internal->type_description_count; ++i) {
+    SpvReflectTypeDescription* p_type = &p_module->_internal->type_descriptions[i];
+    if (IsNotNull(p_type->members)) {
+      SafeFreeTypes(p_type);
+    }
+    SafeFree(p_type->members);
+  }
+  SafeFree(p_module->_internal->type_descriptions);
+
+  // Free SPIR-V code if there was a copy
+  if ((p_module->_internal->module_flags & SPV_REFLECT_MODULE_FLAG_NO_COPY) == 0) {
+    SafeFree(p_module->_internal->spirv_code);
+  }
+  // Free internal
+  SafeFree(p_module->_internal);
+}
+
+uint32_t spvReflectGetCodeSize(const SpvReflectShaderModule* p_module)
+{
+  if (IsNull(p_module)) {
+    return 0;
+  }
+
+  return (uint32_t)(p_module->_internal->spirv_size);
+}
+
+const uint32_t* spvReflectGetCode(const SpvReflectShaderModule* p_module)
+{
+  if (IsNull(p_module)) {
+    return NULL;
+  }
+
+  return p_module->_internal->spirv_code;
+}
+
+const SpvReflectEntryPoint* spvReflectGetEntryPoint(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point
+) {
+  if (IsNull(p_module) || IsNull(entry_point)) {
+    return NULL;
+  }
+
+  for (uint32_t i = 0; i < p_module->entry_point_count; ++i) {
+    if (strcmp(p_module->entry_points[i].name, entry_point) == 0) {
+      return &p_module->entry_points[i];
+    }
+  }
+  return NULL;
+}
+
+SpvReflectResult spvReflectEnumerateDescriptorBindings(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t*                      p_count,
+  SpvReflectDescriptorBinding**  pp_bindings
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (IsNotNull(pp_bindings)) {
+    if (*p_count != p_module->descriptor_binding_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectDescriptorBinding* p_bindings = (SpvReflectDescriptorBinding*)&p_module->descriptor_bindings[index];
+      pp_bindings[index] = p_bindings;
+    }
+  }
+  else {
+    *p_count = p_module->descriptor_binding_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorBindings(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  uint32_t*                      p_count,
+  SpvReflectDescriptorBinding**  pp_bindings
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  uint32_t count = 0;
+  for (uint32_t i = 0; i < p_module->descriptor_binding_count; ++i) {
+    bool found = SearchSortedUint32(
+      p_entry->used_uniforms,
+      p_entry->used_uniform_count,
+      p_module->descriptor_bindings[i].spirv_id);
+    if (found) {
+      if (IsNotNull(pp_bindings)) {
+        if (count >= *p_count) {
+          return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+        }
+        pp_bindings[count++] = (SpvReflectDescriptorBinding*)&p_module->descriptor_bindings[i];
+      } else {
+        ++count;
+      }
+    }
+  }
+  if (IsNotNull(pp_bindings)) {
+    if (count != *p_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+  } else {
+    *p_count = count;
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (IsNotNull(pp_sets)) {
+    if (*p_count != p_module->descriptor_set_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectDescriptorSet* p_set = (SpvReflectDescriptorSet*)&p_module->descriptor_sets[index];
+      pp_sets[index] = p_set;
+    }
+  }
+  else {
+    *p_count = p_module->descriptor_set_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  if (IsNotNull(pp_sets)) {
+    if (*p_count != p_entry->descriptor_set_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectDescriptorSet* p_set = (SpvReflectDescriptorSet*)&p_entry->descriptor_sets[index];
+      pp_sets[index] = p_set;
+    }
+  }
+  else {
+    *p_count = p_entry->descriptor_set_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_module->interface_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = &p_module->interface_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_module->interface_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_entry->interface_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = &p_entry->interface_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_entry->interface_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateInputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_module->input_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = p_module->input_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_module->input_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointInputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_entry->input_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = p_entry->input_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_entry->input_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_module->output_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = p_module->output_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_module->output_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  if (IsNotNull(pp_variables)) {
+    if (*p_count != p_entry->output_variable_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectInterfaceVariable* p_var = p_entry->output_variables[index];
+      pp_variables[index] = p_var;
+    }
+  }
+  else {
+    *p_count = p_entry->output_variable_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectEnumeratePushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  if (pp_blocks != NULL) {
+    if (*p_count != p_module->push_constant_block_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+
+    for (uint32_t index = 0; index < *p_count; ++index) {
+      SpvReflectBlockVariable* p_push_constant_blocks = (SpvReflectBlockVariable*)&p_module->push_constant_blocks[index];
+      pp_blocks[index] = p_push_constant_blocks;
+    }
+  }
+  else {
+    *p_count = p_module->push_constant_block_count;
+  }
+
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+SpvReflectResult spvReflectEnumeratePushConstants(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+)
+{
+  return spvReflectEnumeratePushConstantBlocks(p_module, p_count, pp_blocks);
+}
+
+SpvReflectResult spvReflectEnumerateEntryPointPushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_count)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+  }
+
+  uint32_t count = 0;
+  for (uint32_t i = 0; i < p_module->push_constant_block_count; ++i) {
+    bool found = SearchSortedUint32(p_entry->used_push_constants,
+                           p_entry->used_push_constant_count,
+                           p_module->push_constant_blocks[i].spirv_id);
+    if (found) {
+      if (IsNotNull(pp_blocks)) {
+        if (count >= *p_count) {
+          return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+        }
+        pp_blocks[count++] = (SpvReflectBlockVariable*)&p_module->push_constant_blocks[i];
+      } else {
+        ++count;
+      }
+    }
+  }
+  if (IsNotNull(pp_blocks)) {
+    if (count != *p_count) {
+      return SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH;
+    }
+  } else {
+    *p_count = count;
+  }
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+const SpvReflectDescriptorBinding* spvReflectGetDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+)
+{
+  const SpvReflectDescriptorBinding* p_descriptor = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) {
+      const SpvReflectDescriptorBinding* p_potential = &p_module->descriptor_bindings[index];
+      if ((p_potential->binding == binding_number) && (p_potential->set == set_number)) {
+        p_descriptor = p_potential;
+        break;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_descriptor)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_descriptor;
+}
+
+const SpvReflectDescriptorBinding* spvReflectGetEntryPointDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+)
+{
+  const SpvReflectEntryPoint* p_entry =
+      spvReflectGetEntryPoint(p_module, entry_point);
+  if (IsNull(p_entry)) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectDescriptorBinding* p_descriptor = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) {
+      const SpvReflectDescriptorBinding* p_potential = &p_module->descriptor_bindings[index];
+      bool found = SearchSortedUint32(
+        p_entry->used_uniforms, 
+        p_entry->used_uniform_count,
+        p_potential->spirv_id);
+      if ((p_potential->binding == binding_number) && (p_potential->set == set_number) && found) {
+        p_descriptor = p_potential;
+        break;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_descriptor)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_descriptor;
+}
+
+const SpvReflectDescriptorSet* spvReflectGetDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+)
+{
+  const SpvReflectDescriptorSet* p_set = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->descriptor_set_count; ++index) {
+      const SpvReflectDescriptorSet* p_potential = &p_module->descriptor_sets[index];
+      if (p_potential->set == set_number) {
+        p_set = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_set)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_set;
+}
+
+const SpvReflectDescriptorSet* spvReflectGetEntryPointDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result)
+{
+  const SpvReflectDescriptorSet* p_set = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t index = 0; index < p_entry->descriptor_set_count; ++index) {
+      const SpvReflectDescriptorSet* p_potential = &p_entry->descriptor_sets[index];
+      if (p_potential->set == set_number) {
+        p_set = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_set)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_set;
+}
+
+
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+)
+{
+  if (location == INVALID_VALUE) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->input_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_module->input_variables[index];
+      if (p_potential->location == location) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+const SpvReflectInterfaceVariable* spvReflectGetInputVariable(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+)
+{
+  return spvReflectGetInputVariableByLocation(p_module, location, p_result);
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+)
+{
+  if (location == INVALID_VALUE) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry =
+        spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t index = 0; index < p_entry->input_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_entry->input_variables[index];
+      if (p_potential->location == location) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+)
+{
+  if (IsNull(semantic)) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+    }
+    return NULL;
+  }
+  if (semantic[0] == '\0') {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->input_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_module->input_variables[index];
+      if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+      ?  SPV_REFLECT_RESULT_SUCCESS
+      : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+        : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+)
+{
+  if (IsNull(semantic)) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+    }
+    return NULL;
+  }
+  if (semantic[0] == '\0') {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t index = 0; index < p_entry->input_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_entry->input_variables[index];
+      if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+      ?  SPV_REFLECT_RESULT_SUCCESS
+      : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+        : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableByLocation(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+)
+{
+  if (location == INVALID_VALUE) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->output_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_module->output_variables[index];
+      if (p_potential->location == location) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariable(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+)
+{
+  return spvReflectGetOutputVariableByLocation(p_module, location, p_result);
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+)
+{
+  if (location == INVALID_VALUE) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t index = 0; index < p_entry->output_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_entry->output_variables[index];
+      if (p_potential->location == location) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableBySemantic(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    semantic,
+  SpvReflectResult*              p_result
+)
+{
+  if (IsNull(semantic)) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+    }
+    return NULL;
+  }
+  if (semantic[0] == '\0') {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    for (uint32_t index = 0; index < p_module->output_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_module->output_variables[index];
+      if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  const char*                   semantic,
+  SpvReflectResult*             p_result)
+{
+  if (IsNull(semantic)) {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+    }
+    return NULL;
+  }
+  if (semantic[0] == '\0') {
+    if (IsNotNull(p_result)) {
+      *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+    }
+    return NULL;
+  }
+  const SpvReflectInterfaceVariable* p_var = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry = spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t index = 0; index < p_entry->output_variable_count; ++index) {
+      const SpvReflectInterfaceVariable* p_potential = p_entry->output_variables[index];
+      if (p_potential->semantic != NULL && strcmp(p_potential->semantic, semantic) == 0) {
+        p_var = p_potential;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_var)
+      ?  SPV_REFLECT_RESULT_SUCCESS
+      : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+        : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_var;
+}
+
+const SpvReflectBlockVariable* spvReflectGetPushConstantBlock(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      index,
+  SpvReflectResult*             p_result
+)
+{
+  const SpvReflectBlockVariable* p_push_constant = NULL;
+  if (IsNotNull(p_module)) {
+    if (index < p_module->push_constant_block_count) {
+      p_push_constant = &p_module->push_constant_blocks[index];
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_push_constant)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_push_constant;
+}
+const SpvReflectBlockVariable* spvReflectGetPushConstant(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      index,
+  SpvReflectResult*             p_result
+)
+{
+  return spvReflectGetPushConstantBlock(p_module, index, p_result);
+}
+
+const SpvReflectBlockVariable* spvReflectGetEntryPointPushConstantBlock(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  SpvReflectResult*              p_result)
+{
+  const SpvReflectBlockVariable* p_push_constant = NULL;
+  if (IsNotNull(p_module)) {
+    const SpvReflectEntryPoint* p_entry =
+        spvReflectGetEntryPoint(p_module, entry_point);
+    if (IsNull(p_entry)) {
+      if (IsNotNull(p_result)) {
+        *p_result = SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+      }
+      return NULL;
+    }
+    for (uint32_t i = 0; i < p_module->push_constant_block_count; ++i) {
+      bool found = SearchSortedUint32(
+        p_entry->used_push_constants,
+        p_entry->used_push_constant_count,
+        p_module->push_constant_blocks[i].spirv_id);
+      if (found) {
+        p_push_constant = &p_module->push_constant_blocks[i];
+        break;
+      }
+    }
+  }
+  if (IsNotNull(p_result)) {
+    *p_result = IsNotNull(p_push_constant)
+        ?  SPV_REFLECT_RESULT_SUCCESS
+        : (IsNull(p_module) ? SPV_REFLECT_RESULT_ERROR_NULL_POINTER
+                            : SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND);
+  }
+  return p_push_constant;
+}
+
+SpvReflectResult spvReflectChangeDescriptorBindingNumbers(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           new_set_binding
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_binding)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+
+  SpvReflectDescriptorBinding* p_target_descriptor = NULL;
+  for (uint32_t index = 0; index < p_module->descriptor_binding_count; ++index) {
+    if(&p_module->descriptor_bindings[index] == p_binding) {
+      p_target_descriptor = &p_module->descriptor_bindings[index];
+      break;
+    }
+  }
+
+  if (IsNotNull(p_target_descriptor)) {
+    if (p_target_descriptor->word_offset.binding > (p_module->_internal->spirv_word_count - 1)) {
+      return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED;
+    }
+    // Binding number
+    if (new_binding_number != (uint32_t)SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE) {
+      uint32_t* p_code = p_module->_internal->spirv_code + p_target_descriptor->word_offset.binding;
+      *p_code = new_binding_number;
+      p_target_descriptor->binding = new_binding_number;
+    }
+    // Set number
+    if (new_set_binding != (uint32_t)SPV_REFLECT_SET_NUMBER_DONT_CHANGE) {
+      uint32_t* p_code = p_module->_internal->spirv_code + p_target_descriptor->word_offset.set;
+      *p_code = new_set_binding;
+      p_target_descriptor->set = new_set_binding;
+    }
+  }
+
+  SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS;
+  if (new_set_binding != (uint32_t)SPV_REFLECT_SET_NUMBER_DONT_CHANGE) {
+    result = SynchronizeDescriptorSets(p_module);
+  }
+  return result;
+}
+SpvReflectResult spvReflectChangeDescriptorBindingNumber(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_descriptor_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           optional_new_set_number
+)
+{
+  return spvReflectChangeDescriptorBindingNumbers(
+    p_module,p_descriptor_binding, 
+    new_binding_number, 
+    optional_new_set_number);
+}
+
+SpvReflectResult spvReflectChangeDescriptorSetNumber(
+  SpvReflectShaderModule*        p_module,
+  const SpvReflectDescriptorSet* p_set,
+  uint32_t                       new_set_number
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_set)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  SpvReflectDescriptorSet* p_target_set = NULL;
+  for (uint32_t index = 0; index < SPV_REFLECT_MAX_DESCRIPTOR_SETS; ++index) {
+    // The descriptor sets for specific entry points might not be in this set,
+    // so just match on set index.
+    if (p_module->descriptor_sets[index].set == p_set->set) {
+      p_target_set = (SpvReflectDescriptorSet*)p_set;
+      break;
+    }
+  }
+
+  SpvReflectResult result = SPV_REFLECT_RESULT_SUCCESS;
+  if (IsNotNull(p_target_set) && new_set_number != (uint32_t)SPV_REFLECT_SET_NUMBER_DONT_CHANGE) {
+    for (uint32_t index = 0; index < p_target_set->binding_count; ++index) {
+      SpvReflectDescriptorBinding* p_descriptor = p_target_set->bindings[index];
+      if (p_descriptor->word_offset.set > (p_module->_internal->spirv_word_count - 1)) {
+        return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED;
+      }
+
+      uint32_t* p_code = p_module->_internal->spirv_code + p_descriptor->word_offset.set;
+      *p_code = new_set_number;
+      p_descriptor->set = new_set_number;
+    }
+
+    result = SynchronizeDescriptorSets(p_module);
+  }
+
+  return result;
+}
+
+static SpvReflectResult ChangeVariableLocation(
+  SpvReflectShaderModule*      p_module,
+  SpvReflectInterfaceVariable* p_variable,
+  uint32_t                     new_location
+)
+{
+  if (p_variable->word_offset.location > (p_module->_internal->spirv_word_count - 1)) {
+    return SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED;
+  }
+  uint32_t* p_code = p_module->_internal->spirv_code + p_variable->word_offset.location;
+  *p_code = new_location;
+  p_variable->location = new_location;
+  return SPV_REFLECT_RESULT_SUCCESS;
+}
+
+SpvReflectResult spvReflectChangeInputVariableLocation(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectInterfaceVariable* p_input_variable,
+  uint32_t                           new_location
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_input_variable)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  for (uint32_t index = 0; index < p_module->input_variable_count; ++index) {
+    if(p_module->input_variables[index] == p_input_variable) {
+      return ChangeVariableLocation(p_module, p_module->input_variables[index], new_location);
+    }
+  }
+  return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+}
+
+SpvReflectResult spvReflectChangeOutputVariableLocation(
+  SpvReflectShaderModule*             p_module,
+  const SpvReflectInterfaceVariable*  p_output_variable,
+  uint32_t                            new_location
+)
+{
+  if (IsNull(p_module)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  if (IsNull(p_output_variable)) {
+    return SPV_REFLECT_RESULT_ERROR_NULL_POINTER;
+  }
+  for (uint32_t index = 0; index < p_module->output_variable_count; ++index) {
+    if(p_module->output_variables[index] == p_output_variable) {
+      return ChangeVariableLocation(p_module, p_module->output_variables[index], new_location);
+    }
+  }
+  return SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND;
+}
+
+const char* spvReflectSourceLanguage(SpvSourceLanguage source_lang)
+{
+  switch (source_lang) {
+    case SpvSourceLanguageUnknown        : return "Unknown";
+    case SpvSourceLanguageESSL           : return "ESSL";
+    case SpvSourceLanguageGLSL           : return "GLSL";
+    case SpvSourceLanguageOpenCL_C       : return "OpenCL_C";
+    case SpvSourceLanguageOpenCL_CPP     : return "OpenCL_CPP";
+    case SpvSourceLanguageHLSL           : return "HLSL";
+    case SpvSourceLanguageCPP_for_OpenCL : return "CPP_for_OpenCL";
+    case SpvSourceLanguageSYCL           : return "SYCL";
+    case SpvSourceLanguageMax:
+      break;
+  }
+  return "";
+}
+
+const char* spvReflectBlockVariableTypeName(
+  const SpvReflectBlockVariable* p_var
+)
+{
+    if (p_var == NULL) {
+        return NULL;
+    }
+    return p_var->type_description->type_name;
+}
diff --git a/cbits/spirv_reflect.h b/cbits/spirv_reflect.h
new file mode 100644
--- /dev/null
+++ b/cbits/spirv_reflect.h
@@ -0,0 +1,2292 @@
+/*
+ Copyright 2017-2022 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/*
+
+VERSION HISTORY
+
+  1.0   (2018-03-27) Initial public release
+
+*/
+
+/*!
+
+ @file spirv_reflect.h
+
+*/
+#ifndef SPIRV_REFLECT_H
+#define SPIRV_REFLECT_H
+
+#if defined(SPIRV_REFLECT_USE_SYSTEM_SPIRV_H)
+#include <spirv/unified1/spirv.h>
+#else
+#include "./include/spirv/unified1/spirv.h"
+#endif
+
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+  #define SPV_REFLECT_DEPRECATED(msg_str) __declspec(deprecated("This symbol is deprecated. Details: " msg_str))
+#elif defined(__clang__)
+  #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated(msg_str)))
+#elif defined(__GNUC__)
+  #if GCC_VERSION >= 40500
+    #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated(msg_str)))
+  #else
+    #define SPV_REFLECT_DEPRECATED(msg_str) __attribute__((deprecated))
+  #endif
+#else
+  #define SPV_REFLECT_DEPRECATED(msg_str)
+#endif
+
+/*! @enum SpvReflectResult
+
+*/
+typedef enum SpvReflectResult {
+  SPV_REFLECT_RESULT_SUCCESS,
+  SPV_REFLECT_RESULT_NOT_READY,
+  SPV_REFLECT_RESULT_ERROR_PARSE_FAILED,
+  SPV_REFLECT_RESULT_ERROR_ALLOC_FAILED,
+  SPV_REFLECT_RESULT_ERROR_RANGE_EXCEEDED,
+  SPV_REFLECT_RESULT_ERROR_NULL_POINTER,
+  SPV_REFLECT_RESULT_ERROR_INTERNAL_ERROR,
+  SPV_REFLECT_RESULT_ERROR_COUNT_MISMATCH,
+  SPV_REFLECT_RESULT_ERROR_ELEMENT_NOT_FOUND,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_CODE_SIZE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_MAGIC_NUMBER,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_EOF,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ID_REFERENCE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_SET_NUMBER_OVERFLOW,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_STORAGE_CLASS,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_RECURSION,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_INSTRUCTION,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_UNEXPECTED_BLOCK_DATA,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_BLOCK_MEMBER_REFERENCE,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_ENTRY_POINT,
+  SPV_REFLECT_RESULT_ERROR_SPIRV_INVALID_EXECUTION_MODE,
+} SpvReflectResult;
+
+/*! @enum SpvReflectModuleFlagBits
+
+SPV_REFLECT_MODULE_FLAG_NO_COPY - Disables copying of SPIR-V code 
+  when a SPIRV-Reflect shader module is created. It is the 
+  responsibility of the calling program to ensure that the pointer
+  remains valid and the memory it's pointing to is not freed while
+  SPIRV-Reflect operations are taking place. Freeing the backing 
+  memory will cause undefined behavior or most likely a crash.
+  This is flag is intended for cases where the memory overhead of
+  storing the copied SPIR-V is undesirable.
+
+*/
+typedef enum SpvReflectModuleFlagBits {
+  SPV_REFLECT_MODULE_FLAG_NONE    = 0x00000000,
+  SPV_REFLECT_MODULE_FLAG_NO_COPY = 0x00000001,
+} SpvReflectModuleFlagBits;
+
+typedef uint32_t SpvReflectModuleFlags;
+
+/*! @enum SpvReflectTypeFlagBits
+
+*/
+typedef enum SpvReflectTypeFlagBits {
+  SPV_REFLECT_TYPE_FLAG_UNDEFINED                       = 0x00000000,
+  SPV_REFLECT_TYPE_FLAG_VOID                            = 0x00000001,
+  SPV_REFLECT_TYPE_FLAG_BOOL                            = 0x00000002,
+  SPV_REFLECT_TYPE_FLAG_INT                             = 0x00000004,
+  SPV_REFLECT_TYPE_FLAG_FLOAT                           = 0x00000008,
+  SPV_REFLECT_TYPE_FLAG_VECTOR                          = 0x00000100,
+  SPV_REFLECT_TYPE_FLAG_MATRIX                          = 0x00000200,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_IMAGE                  = 0x00010000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLER                = 0x00020000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_SAMPLED_IMAGE          = 0x00040000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_BLOCK                  = 0x00080000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_ACCELERATION_STRUCTURE = 0x00100000,
+  SPV_REFLECT_TYPE_FLAG_EXTERNAL_MASK                   = 0x00FF0000,
+  SPV_REFLECT_TYPE_FLAG_STRUCT                          = 0x10000000,
+  SPV_REFLECT_TYPE_FLAG_ARRAY                           = 0x20000000,
+} SpvReflectTypeFlagBits;
+
+typedef uint32_t SpvReflectTypeFlags;
+
+/*! @enum SpvReflectDecorationBits
+
+NOTE: HLSL row_major and column_major decorations are reversed
+      in SPIR-V. Meaning that matrices declrations with row_major
+      will get reflected as column_major and vice versa. The
+      row and column decorations get appied during the compilation.
+      SPIRV-Reflect reads the data as is and does not make any
+      attempt to correct it to match what's in the source.
+
+*/
+typedef enum SpvReflectDecorationFlagBits {
+  SPV_REFLECT_DECORATION_NONE                   = 0x00000000,
+  SPV_REFLECT_DECORATION_BLOCK                  = 0x00000001,
+  SPV_REFLECT_DECORATION_BUFFER_BLOCK           = 0x00000002,
+  SPV_REFLECT_DECORATION_ROW_MAJOR              = 0x00000004,
+  SPV_REFLECT_DECORATION_COLUMN_MAJOR           = 0x00000008,
+  SPV_REFLECT_DECORATION_BUILT_IN               = 0x00000010,
+  SPV_REFLECT_DECORATION_NOPERSPECTIVE          = 0x00000020,
+  SPV_REFLECT_DECORATION_FLAT                   = 0x00000040,
+  SPV_REFLECT_DECORATION_NON_WRITABLE           = 0x00000080,
+  SPV_REFLECT_DECORATION_RELAXED_PRECISION      = 0x00000100,
+  SPV_REFLECT_DECORATION_NON_READABLE           = 0x00000200,
+} SpvReflectDecorationFlagBits;
+
+typedef uint32_t SpvReflectDecorationFlags;
+
+/*! @enum SpvReflectResourceType
+
+*/
+typedef enum SpvReflectResourceType {
+  SPV_REFLECT_RESOURCE_FLAG_UNDEFINED           = 0x00000000,
+  SPV_REFLECT_RESOURCE_FLAG_SAMPLER             = 0x00000001,
+  SPV_REFLECT_RESOURCE_FLAG_CBV                 = 0x00000002,
+  SPV_REFLECT_RESOURCE_FLAG_SRV                 = 0x00000004,
+  SPV_REFLECT_RESOURCE_FLAG_UAV                 = 0x00000008,
+} SpvReflectResourceType;
+
+/*! @enum SpvReflectFormat
+
+*/
+typedef enum SpvReflectFormat {
+  SPV_REFLECT_FORMAT_UNDEFINED           =   0, // = VK_FORMAT_UNDEFINED
+  SPV_REFLECT_FORMAT_R32_UINT            =  98, // = VK_FORMAT_R32_UINT
+  SPV_REFLECT_FORMAT_R32_SINT            =  99, // = VK_FORMAT_R32_SINT
+  SPV_REFLECT_FORMAT_R32_SFLOAT          = 100, // = VK_FORMAT_R32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32_UINT         = 101, // = VK_FORMAT_R32G32_UINT
+  SPV_REFLECT_FORMAT_R32G32_SINT         = 102, // = VK_FORMAT_R32G32_SINT
+  SPV_REFLECT_FORMAT_R32G32_SFLOAT       = 103, // = VK_FORMAT_R32G32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32B32_UINT      = 104, // = VK_FORMAT_R32G32B32_UINT
+  SPV_REFLECT_FORMAT_R32G32B32_SINT      = 105, // = VK_FORMAT_R32G32B32_SINT
+  SPV_REFLECT_FORMAT_R32G32B32_SFLOAT    = 106, // = VK_FORMAT_R32G32B32_SFLOAT
+  SPV_REFLECT_FORMAT_R32G32B32A32_UINT   = 107, // = VK_FORMAT_R32G32B32A32_UINT
+  SPV_REFLECT_FORMAT_R32G32B32A32_SINT   = 108, // = VK_FORMAT_R32G32B32A32_SINT
+  SPV_REFLECT_FORMAT_R32G32B32A32_SFLOAT = 109, // = VK_FORMAT_R32G32B32A32_SFLOAT
+  SPV_REFLECT_FORMAT_R64_UINT            = 110, // = VK_FORMAT_R64_UINT
+  SPV_REFLECT_FORMAT_R64_SINT            = 111, // = VK_FORMAT_R64_SINT
+  SPV_REFLECT_FORMAT_R64_SFLOAT          = 112, // = VK_FORMAT_R64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64_UINT         = 113, // = VK_FORMAT_R64G64_UINT
+  SPV_REFLECT_FORMAT_R64G64_SINT         = 114, // = VK_FORMAT_R64G64_SINT
+  SPV_REFLECT_FORMAT_R64G64_SFLOAT       = 115, // = VK_FORMAT_R64G64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64B64_UINT      = 116, // = VK_FORMAT_R64G64B64_UINT
+  SPV_REFLECT_FORMAT_R64G64B64_SINT      = 117, // = VK_FORMAT_R64G64B64_SINT
+  SPV_REFLECT_FORMAT_R64G64B64_SFLOAT    = 118, // = VK_FORMAT_R64G64B64_SFLOAT
+  SPV_REFLECT_FORMAT_R64G64B64A64_UINT   = 119, // = VK_FORMAT_R64G64B64A64_UINT
+  SPV_REFLECT_FORMAT_R64G64B64A64_SINT   = 120, // = VK_FORMAT_R64G64B64A64_SINT
+  SPV_REFLECT_FORMAT_R64G64B64A64_SFLOAT = 121, // = VK_FORMAT_R64G64B64A64_SFLOAT
+} SpvReflectFormat;
+
+/*! @enum SpvReflectVariableFlagBits
+
+*/
+enum SpvReflectVariableFlagBits{
+  SPV_REFLECT_VARIABLE_FLAGS_NONE   = 0x00000000,
+  SPV_REFLECT_VARIABLE_FLAGS_UNUSED = 0x00000001,
+};
+
+typedef uint32_t SpvReflectVariableFlags;
+
+/*! @enum SpvReflectDescriptorType
+
+*/
+typedef enum SpvReflectDescriptorType {
+  SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER                    =  0,        // = VK_DESCRIPTOR_TYPE_SAMPLER
+  SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER     =  1,        // = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
+  SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE              =  2,        // = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE              =  3,        // = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER       =  4,        // = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER       =  5,        // = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER             =  6,        // = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER             =  7,        // = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
+  SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC     =  8,        // = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+  SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC     =  9,        // = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC
+  SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT           = 10,        // = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
+  SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000 // = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR
+} SpvReflectDescriptorType;
+
+/*! @enum SpvReflectShaderStageFlagBits
+
+*/
+typedef enum SpvReflectShaderStageFlagBits {
+  SPV_REFLECT_SHADER_STAGE_VERTEX_BIT                  = 0x00000001, // = VK_SHADER_STAGE_VERTEX_BIT
+  SPV_REFLECT_SHADER_STAGE_TESSELLATION_CONTROL_BIT    = 0x00000002, // = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT
+  SPV_REFLECT_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, // = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT
+  SPV_REFLECT_SHADER_STAGE_GEOMETRY_BIT                = 0x00000008, // = VK_SHADER_STAGE_GEOMETRY_BIT
+  SPV_REFLECT_SHADER_STAGE_FRAGMENT_BIT                = 0x00000010, // = VK_SHADER_STAGE_FRAGMENT_BIT
+  SPV_REFLECT_SHADER_STAGE_COMPUTE_BIT                 = 0x00000020, // = VK_SHADER_STAGE_COMPUTE_BIT
+  SPV_REFLECT_SHADER_STAGE_TASK_BIT_NV                 = 0x00000040, // = VK_SHADER_STAGE_TASK_BIT_NV
+  SPV_REFLECT_SHADER_STAGE_MESH_BIT_NV                 = 0x00000080, // = VK_SHADER_STAGE_MESH_BIT_NV
+  SPV_REFLECT_SHADER_STAGE_RAYGEN_BIT_KHR              = 0x00000100, // = VK_SHADER_STAGE_RAYGEN_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_ANY_HIT_BIT_KHR             = 0x00000200, // = VK_SHADER_STAGE_ANY_HIT_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_CLOSEST_HIT_BIT_KHR         = 0x00000400, // = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_MISS_BIT_KHR                = 0x00000800, // = VK_SHADER_STAGE_MISS_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_INTERSECTION_BIT_KHR        = 0x00001000, // = VK_SHADER_STAGE_INTERSECTION_BIT_KHR
+  SPV_REFLECT_SHADER_STAGE_CALLABLE_BIT_KHR            = 0x00002000, // = VK_SHADER_STAGE_CALLABLE_BIT_KHR
+
+} SpvReflectShaderStageFlagBits;
+
+/*! @enum SpvReflectGenerator
+
+*/
+typedef enum SpvReflectGenerator {
+  SPV_REFLECT_GENERATOR_KHRONOS_LLVM_SPIRV_TRANSLATOR         = 6,
+  SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_ASSEMBLER         = 7,
+  SPV_REFLECT_GENERATOR_KHRONOS_GLSLANG_REFERENCE_FRONT_END   = 8,
+  SPV_REFLECT_GENERATOR_GOOGLE_SHADERC_OVER_GLSLANG           = 13,
+  SPV_REFLECT_GENERATOR_GOOGLE_SPIREGG                        = 14,
+  SPV_REFLECT_GENERATOR_GOOGLE_RSPIRV                         = 15,
+  SPV_REFLECT_GENERATOR_X_LEGEND_MESA_MESAIR_SPIRV_TRANSLATOR = 16,
+  SPV_REFLECT_GENERATOR_KHRONOS_SPIRV_TOOLS_LINKER            = 17,
+  SPV_REFLECT_GENERATOR_WINE_VKD3D_SHADER_COMPILER            = 18,
+  SPV_REFLECT_GENERATOR_CLAY_CLAY_SHADER_COMPILER             = 19,
+} SpvReflectGenerator;
+
+enum {
+  SPV_REFLECT_MAX_ARRAY_DIMS                    = 32,
+  SPV_REFLECT_MAX_DESCRIPTOR_SETS               = 64,
+};
+
+enum {
+  SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE        = ~0,
+  SPV_REFLECT_SET_NUMBER_DONT_CHANGE            = ~0
+};
+
+typedef struct SpvReflectNumericTraits {
+  struct Scalar {
+    uint32_t                        width;
+    uint32_t                        signedness;
+  } scalar;
+
+  struct Vector {
+    uint32_t                        component_count;
+  } vector;
+
+  struct Matrix {
+    uint32_t                        column_count;
+    uint32_t                        row_count;
+    uint32_t                        stride; // Measured in bytes
+  } matrix;
+} SpvReflectNumericTraits;
+
+typedef struct SpvReflectImageTraits {
+  SpvDim                            dim;
+  uint32_t                          depth;
+  uint32_t                          arrayed;
+  uint32_t                          ms; // 0: single-sampled; 1: multisampled
+  uint32_t                          sampled;
+  SpvImageFormat                    image_format;
+} SpvReflectImageTraits;
+
+typedef struct SpvReflectArrayTraits {
+  uint32_t                          dims_count;
+  // Each entry is: 0xFFFFFFFF for a specialization constant dimension,
+  // 0 for a runtime array dimension, and the array length otherwise.
+  uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+  // Stores Ids for dimensions that are specialization constants
+  uint32_t                          spec_constant_op_ids[SPV_REFLECT_MAX_ARRAY_DIMS];
+  uint32_t                          stride; // Measured in bytes
+} SpvReflectArrayTraits;
+
+typedef struct SpvReflectBindingArrayTraits {
+  uint32_t                          dims_count;
+  uint32_t                          dims[SPV_REFLECT_MAX_ARRAY_DIMS];
+} SpvReflectBindingArrayTraits;
+
+/*! @struct SpvReflectTypeDescription
+
+*/
+typedef struct SpvReflectTypeDescription {
+  uint32_t                          id;
+  SpvOp                             op;
+  const char*                       type_name;
+  const char*                       struct_member_name;
+  SpvStorageClass                   storage_class;
+  SpvReflectTypeFlags               type_flags;
+  SpvReflectDecorationFlags         decoration_flags;
+
+  struct Traits {
+    SpvReflectNumericTraits         numeric;
+    SpvReflectImageTraits           image;
+    SpvReflectArrayTraits           array;
+  } traits;
+
+  uint32_t                          member_count;
+  struct SpvReflectTypeDescription* members;
+} SpvReflectTypeDescription;
+
+
+/*! @struct SpvReflectInterfaceVariable
+
+*/
+typedef struct SpvReflectInterfaceVariable {
+  uint32_t                            spirv_id;
+  const char*                         name;
+  uint32_t                            location;
+  SpvStorageClass                     storage_class;
+  const char*                         semantic;
+  SpvReflectDecorationFlags           decoration_flags;
+  SpvBuiltIn                          built_in;
+  SpvReflectNumericTraits             numeric;
+  SpvReflectArrayTraits               array;
+
+  uint32_t                            member_count;
+  struct SpvReflectInterfaceVariable* members;
+
+  SpvReflectFormat                    format;
+
+  // NOTE: SPIR-V shares type references for variables
+  //       that have the same underlying type. This means
+  //       that the same type name will appear for multiple
+  //       variables.
+  SpvReflectTypeDescription*          type_description;
+
+  struct {
+    uint32_t                          location;
+  } word_offset;
+} SpvReflectInterfaceVariable;
+
+/*! @struct SpvReflectBlockVariable
+
+*/
+typedef struct SpvReflectBlockVariable {
+  uint32_t                          spirv_id;
+  const char*                       name;
+  uint32_t                          offset;           // Measured in bytes
+  uint32_t                          absolute_offset;  // Measured in bytes
+  uint32_t                          size;             // Measured in bytes
+  uint32_t                          padded_size;      // Measured in bytes
+  SpvReflectDecorationFlags         decoration_flags;
+  SpvReflectNumericTraits           numeric;
+  SpvReflectArrayTraits             array;
+  SpvReflectVariableFlags           flags;
+
+  uint32_t                          member_count;
+  struct SpvReflectBlockVariable*   members;
+
+  SpvReflectTypeDescription*        type_description;
+} SpvReflectBlockVariable;
+
+/*! @struct SpvReflectDescriptorBinding
+
+*/
+typedef struct SpvReflectDescriptorBinding {
+  uint32_t                            spirv_id;
+  const char*                         name;
+  uint32_t                            binding;
+  uint32_t                            input_attachment_index;
+  uint32_t                            set;
+  SpvReflectDescriptorType            descriptor_type;
+  SpvReflectResourceType              resource_type;
+  SpvReflectImageTraits               image;
+  SpvReflectBlockVariable             block;
+  SpvReflectBindingArrayTraits        array;
+  uint32_t                            count;
+  uint32_t                            accessed;
+  uint32_t                            uav_counter_id;
+  struct SpvReflectDescriptorBinding* uav_counter_binding;
+
+  SpvReflectTypeDescription*          type_description;
+
+  struct {
+    uint32_t                          binding;
+    uint32_t                          set;
+  } word_offset;
+
+  SpvReflectDecorationFlags           decoration_flags;
+} SpvReflectDescriptorBinding;
+
+/*! @struct SpvReflectDescriptorSet
+
+*/
+typedef struct SpvReflectDescriptorSet {
+  uint32_t                          set;
+  uint32_t                          binding_count;
+  SpvReflectDescriptorBinding**     bindings;
+} SpvReflectDescriptorSet;
+
+/*! @struct SpvReflectEntryPoint
+
+ */
+typedef struct SpvReflectEntryPoint {
+  const char*                       name;
+  uint32_t                          id;
+
+  SpvExecutionModel                 spirv_execution_model;
+  SpvReflectShaderStageFlagBits     shader_stage;
+
+  uint32_t                          input_variable_count;  
+  SpvReflectInterfaceVariable**     input_variables;       
+  uint32_t                          output_variable_count; 
+  SpvReflectInterfaceVariable**     output_variables;      
+  uint32_t                          interface_variable_count;
+  SpvReflectInterfaceVariable*      interface_variables;
+
+  uint32_t                          descriptor_set_count;
+  SpvReflectDescriptorSet*          descriptor_sets;
+
+  uint32_t                          used_uniform_count;
+  uint32_t*                         used_uniforms;
+  uint32_t                          used_push_constant_count;
+  uint32_t*                         used_push_constants;
+
+  uint32_t                          execution_mode_count;
+  SpvExecutionMode*                 execution_modes;
+
+  struct LocalSize {
+    uint32_t                        x;
+    uint32_t                        y;
+    uint32_t                        z;
+  } local_size;
+  uint32_t                          invocations; // valid for geometry
+  uint32_t                          output_vertices; // valid for geometry, tesselation
+} SpvReflectEntryPoint;
+
+/*! @struct SpvReflectCapability
+
+*/
+typedef struct SpvReflectCapability {
+  SpvCapability                     value;
+  uint32_t                          word_offset;
+} SpvReflectCapability;
+
+/*! @struct SpvReflectShaderModule
+
+*/
+typedef struct SpvReflectShaderModule {
+  SpvReflectGenerator               generator;
+  const char*                       entry_point_name;
+  uint32_t                          entry_point_id;
+  uint32_t                          entry_point_count;
+  SpvReflectEntryPoint*             entry_points;
+  SpvSourceLanguage                 source_language;
+  uint32_t                          source_language_version;
+  const char*                       source_file;
+  const char*                       source_source;
+  uint32_t                          capability_count;
+  SpvReflectCapability*             capabilities;
+  SpvExecutionModel                 spirv_execution_model;                            // Uses value(s) from first entry point
+  SpvReflectShaderStageFlagBits     shader_stage;                                     // Uses value(s) from first entry point
+  uint32_t                          descriptor_binding_count;                         // Uses value(s) from first entry point
+  SpvReflectDescriptorBinding*      descriptor_bindings;                              // Uses value(s) from first entry point
+  uint32_t                          descriptor_set_count;                             // Uses value(s) from first entry point
+  SpvReflectDescriptorSet           descriptor_sets[SPV_REFLECT_MAX_DESCRIPTOR_SETS]; // Uses value(s) from first entry point
+  uint32_t                          input_variable_count;                             // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable**     input_variables;                                  // Uses value(s) from first entry point
+  uint32_t                          output_variable_count;                            // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable**     output_variables;                                 // Uses value(s) from first entry point
+  uint32_t                          interface_variable_count;                         // Uses value(s) from first entry point
+  SpvReflectInterfaceVariable*      interface_variables;                              // Uses value(s) from first entry point
+  uint32_t                          push_constant_block_count;                        // Uses value(s) from first entry point
+  SpvReflectBlockVariable*          push_constant_blocks;                             // Uses value(s) from first entry point
+
+  struct Internal {
+    SpvReflectModuleFlags           module_flags;
+    size_t                          spirv_size;
+    uint32_t*                       spirv_code;
+    uint32_t                        spirv_word_count;
+
+    size_t                          type_description_count;
+    SpvReflectTypeDescription*      type_descriptions;
+  } * _internal;
+
+} SpvReflectShaderModule;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*! @fn spvReflectCreateShaderModule
+
+ @param  size      Size in bytes of SPIR-V code.
+ @param  p_code    Pointer to SPIR-V code.
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           SPV_REFLECT_RESULT_SUCCESS on success.
+
+*/
+SpvReflectResult spvReflectCreateShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+/*! @fn spvReflectCreateShaderModule2
+
+ @param  flags     Flags for module creations.
+ @param  size      Size in bytes of SPIR-V code.
+ @param  p_code    Pointer to SPIR-V code.
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           SPV_REFLECT_RESULT_SUCCESS on success.
+
+*/
+SpvReflectResult spvReflectCreateShaderModule2(
+  SpvReflectModuleFlags    flags,
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+SPV_REFLECT_DEPRECATED("renamed to spvReflectCreateShaderModule")
+SpvReflectResult spvReflectGetShaderModule(
+  size_t                   size,
+  const void*              p_code,
+  SpvReflectShaderModule*  p_module
+);
+
+
+/*! @fn spvReflectDestroyShaderModule
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+
+*/
+void spvReflectDestroyShaderModule(SpvReflectShaderModule* p_module);
+
+
+/*! @fn spvReflectGetCodeSize
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           Returns the size of the SPIR-V in bytes
+
+*/
+uint32_t spvReflectGetCodeSize(const SpvReflectShaderModule* p_module);
+
+
+/*! @fn spvReflectGetCode
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @return           Returns a const pointer to the compiled SPIR-V bytecode.
+
+*/
+const uint32_t* spvReflectGetCode(const SpvReflectShaderModule* p_module);
+
+/*! @fn spvReflectGetEntryPoint
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  Name of the requested entry point.
+ @return              Returns a const pointer to the requested entry point,
+                      or NULL if it's not found.
+*/
+const SpvReflectEntryPoint* spvReflectGetEntryPoint(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point
+);
+
+/*! @fn spvReflectEnumerateDescriptorBindings
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count      If pp_bindings is NULL, the module's descriptor binding
+                      count (across all descriptor sets) will be stored here.
+                      If pp_bindings is not NULL, *p_count must contain the
+                      module's descriptor binding count.
+ @param  pp_bindings  If NULL, the module's total descriptor binding count
+                      will be written to *p_count.
+                      If non-NULL, pp_bindings must point to an array with
+                      *p_count entries, where pointers to the module's
+                      descriptor bindings will be written. The caller must not
+                      free the binding pointers written to this array.
+ @return              If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                      Otherwise, the error code indicates the cause of the
+                      failure.
+
+*/
+SpvReflectResult spvReflectEnumerateDescriptorBindings(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t*                      p_count,
+  SpvReflectDescriptorBinding**  pp_bindings
+);
+
+/*! @fn spvReflectEnumerateEntryPointDescriptorBindings
+ @brief  Creates a listing of all descriptor bindings that are used in the
+         static call tree of the given entry point.
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The name of the entry point to get the descriptor bindings for.
+ @param  p_count      If pp_bindings is NULL, the entry point's descriptor binding
+                      count (across all descriptor sets) will be stored here.
+                      If pp_bindings is not NULL, *p_count must contain the
+                      entry points's descriptor binding count.
+ @param  pp_bindings  If NULL, the entry point's total descriptor binding count
+                      will be written to *p_count.
+                      If non-NULL, pp_bindings must point to an array with
+                      *p_count entries, where pointers to the entry point's
+                      descriptor bindings will be written. The caller must not
+                      free the binding pointers written to this array.
+ @return              If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                      Otherwise, the error code indicates the cause of the
+                      failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorBindings(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+);
+
+/*! @fn spvReflectEnumerateDescriptorSets
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count   If pp_sets is NULL, the module's descriptor set
+                   count will be stored here.
+                   If pp_sets is not NULL, *p_count must contain the
+                   module's descriptor set count.
+ @param  pp_sets   If NULL, the module's total descriptor set count
+                   will be written to *p_count.
+                   If non-NULL, pp_sets must point to an array with
+                   *p_count entries, where pointers to the module's
+                   descriptor sets will be written. The caller must not
+                   free the descriptor set pointers written to this array.
+ @return           If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                   Otherwise, the error code indicates the cause of the
+                   failure.
+
+*/
+SpvReflectResult spvReflectEnumerateDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+);
+
+/*! @fn spvReflectEnumerateEntryPointDescriptorSets
+ @brief  Creates a listing of all descriptor sets and their bindings that are
+         used in the static call tree of a given entry point.
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The name of the entry point to get the descriptor bindings for.
+ @param  p_count     If pp_sets is NULL, the module's descriptor set
+                     count will be stored here.
+                     If pp_sets is not NULL, *p_count must contain the
+                     module's descriptor set count.
+ @param  pp_sets     If NULL, the module's total descriptor set count
+                     will be written to *p_count.
+                     If non-NULL, pp_sets must point to an array with
+                     *p_count entries, where pointers to the module's
+                     descriptor sets will be written. The caller must not
+                     free the descriptor set pointers written to this array.
+ @return             If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                     Otherwise, the error code indicates the cause of the
+                     failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointDescriptorSets(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorSet**     pp_sets
+);
+
+
+/*! @fn spvReflectEnumerateInterfaceVariables
+ @brief  If the module contains multiple entry points, this will only get
+         the interface variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's interface variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's interface variable count.
+ @param  pp_variables  If NULL, the module's interface variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       interface variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointInterfaceVariables
+ @brief  Enumerate the interface variables for a given entry point.
+ @param  entry_point The name of the entry point to get the interface variables for.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the entry point's interface variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's interface variable count.
+ @param  pp_variables  If NULL, the entry point's interface variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       interface variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointInterfaceVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumerateInputVariables
+ @brief  If the module contains multiple entry points, this will only get
+         the input variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's input variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's input variable count.
+ @param  pp_variables  If NULL, the module's input variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       input variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateInputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointInputVariables
+ @brief  Enumerate the input variables for a given entry point.
+ @param  entry_point The name of the entry point to get the input variables for.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the entry point's input variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's input variable count.
+ @param  pp_variables  If NULL, the entry point's input variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       input variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointInputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumerateOutputVariables
+ @brief  Note: If the module contains multiple entry points, this will only get
+         the output variables for the first one.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count       If pp_variables is NULL, the module's output variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the module's output variable count.
+ @param  pp_variables  If NULL, the module's output variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the module's
+                       output variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+/*! @fn spvReflectEnumerateEntryPointOutputVariables
+ @brief  Enumerate the output variables for a given entry point.
+ @param  p_module      Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point   The name of the entry point to get the output variables for.
+ @param  p_count       If pp_variables is NULL, the entry point's output variable
+                       count will be stored here.
+                       If pp_variables is not NULL, *p_count must contain
+                       the entry point's output variable count.
+ @param  pp_variables  If NULL, the entry point's output variable count will be
+                       written to *p_count.
+                       If non-NULL, pp_variables must point to an array with
+                       *p_count entries, where pointers to the entry point's
+                       output variables will be written. The caller must not
+                       free the interface variables written to this array.
+ @return               If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                       Otherwise, the error code indicates the cause of the
+                       failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointOutputVariables(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+);
+
+
+/*! @fn spvReflectEnumeratePushConstantBlocks
+ @brief  Note: If the module contains multiple entry points, this will only get
+         the push constant blocks for the first one.
+ @param  p_module   Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count    If pp_blocks is NULL, the module's push constant
+                    block count will be stored here.
+                    If pp_blocks is not NULL, *p_count must
+                    contain the module's push constant block count.
+ @param  pp_blocks  If NULL, the module's push constant block count
+                    will be written to *p_count.
+                    If non-NULL, pp_blocks must point to an
+                    array with *p_count entries, where pointers to
+                    the module's push constant blocks will be written.
+                    The caller must not free the block variables written
+                    to this array.
+ @return            If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                    Otherwise, the error code indicates the cause of the
+                    failure.
+
+*/
+SpvReflectResult spvReflectEnumeratePushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectEnumeratePushConstantBlocks")
+SpvReflectResult spvReflectEnumeratePushConstants(
+  const SpvReflectShaderModule* p_module,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+
+/*! @fn spvReflectEnumerateEntryPointPushConstantBlocks
+ @brief  Enumerate the push constant blocks used in the static call tree of a
+         given entry point.
+ @param  p_module   Pointer to an instance of SpvReflectShaderModule.
+ @param  p_count    If pp_blocks is NULL, the entry point's push constant
+                    block count will be stored here.
+                    If pp_blocks is not NULL, *p_count must
+                    contain the entry point's push constant block count.
+ @param  pp_blocks  If NULL, the entry point's push constant block count
+                    will be written to *p_count.
+                    If non-NULL, pp_blocks must point to an
+                    array with *p_count entries, where pointers to
+                    the entry point's push constant blocks will be written.
+                    The caller must not free the block variables written
+                    to this array.
+ @return            If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                    Otherwise, the error code indicates the cause of the
+                    failure.
+
+*/
+SpvReflectResult spvReflectEnumerateEntryPointPushConstantBlocks(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectBlockVariable**     pp_blocks
+);
+
+
+/*! @fn spvReflectGetDescriptorBinding
+
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  binding_number  The "binding" value of the requested descriptor
+                         binding.
+ @param  set_number      The "set" value of the requested descriptor binding.
+ @param  p_result        If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                         written to *p_result. Otherwise, a error code
+                         indicating the cause of the failure will be stored
+                         here.
+ @return                 If the module contains a descriptor binding that
+                         matches the provided [binding_number, set_number]
+                         values, a pointer to that binding is returned. The
+                         caller must not free this pointer.
+                         If no match can be found, or if an unrelated error
+                         occurs, the return value will be NULL. Detailed
+                         error results are written to *pResult.
+@note                    If the module contains multiple desriptor bindings
+                         with the same set and binding numbers, there are
+                         no guarantees about which binding will be returned.
+
+*/
+const SpvReflectDescriptorBinding* spvReflectGetDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+/*! @fn spvReflectGetEntryPointDescriptorBinding
+ @brief  Get the descriptor binding with the given binding number and set
+         number that is used in the static call tree of a certain entry
+         point.
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point     The entry point to get the binding from.
+ @param  binding_number  The "binding" value of the requested descriptor
+                         binding.
+ @param  set_number      The "set" value of the requested descriptor binding.
+ @param  p_result        If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                         written to *p_result. Otherwise, a error code
+                         indicating the cause of the failure will be stored
+                         here.
+ @return                 If the entry point contains a descriptor binding that
+                         matches the provided [binding_number, set_number]
+                         values, a pointer to that binding is returned. The
+                         caller must not free this pointer.
+                         If no match can be found, or if an unrelated error
+                         occurs, the return value will be NULL. Detailed
+                         error results are written to *pResult.
+@note                    If the entry point contains multiple desriptor bindings
+                         with the same set and binding numbers, there are
+                         no guarantees about which binding will be returned.
+
+*/
+const SpvReflectDescriptorBinding* spvReflectGetEntryPointDescriptorBinding(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      binding_number,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+
+/*! @fn spvReflectGetDescriptorSet
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  set_number  The "set" value of the requested descriptor set.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the module contains a descriptor set with the
+                     provided set_number, a pointer to that set is
+                     returned. The caller must not free this pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+
+*/
+const SpvReflectDescriptorSet* spvReflectGetDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+/*! @fn spvReflectGetEntryPointDescriptorSet
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the descriptor set from.
+ @param  set_number  The "set" value of the requested descriptor set.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the entry point contains a descriptor set with the
+                     provided set_number, a pointer to that set is
+                     returned. The caller must not free this pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+
+*/
+const SpvReflectDescriptorSet* spvReflectGetEntryPointDescriptorSet(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      set_number,
+  SpvReflectResult*             p_result
+);
+
+
+/* @fn spvReflectGetInputVariableByLocation
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  location  The "location" value of the requested input variable.
+                   A location of 0xFFFFFFFF will always return NULL
+                   with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an input interface variable
+                   with the provided location value, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetInputVariableByLocation")
+const SpvReflectInterfaceVariable* spvReflectGetInputVariable(
+  const SpvReflectShaderModule* p_module,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetEntryPointInputVariableByLocation
+
+ @param  p_module    Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the input variable from.
+ @param  location    The "location" value of the requested input variable.
+                     A location of 0xFFFFFFFF will always return NULL
+                     with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result    If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                     written to *p_result. Otherwise, a error code
+                     indicating the cause of the failure will be stored
+                     here.
+ @return             If the entry point contains an input interface variable
+                     with the provided location value, a pointer to that
+                     variable is returned. The caller must not free this
+                     pointer.
+                     If no match can be found, or if an unrelated error
+                     occurs, the return value will be NULL. Detailed
+                     error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableByLocation(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  uint32_t                      location,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetInputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  semantic  The "semantic" value of the requested input variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an input interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetEntryPointInputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point The entry point to get the input variable from.
+ @param  semantic  The "semantic" value of the requested input variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the entry point contains an input interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointInputVariableBySemantic(
+  const SpvReflectShaderModule* p_module,
+  const char*                   entry_point,
+  const char*                   semantic,
+  SpvReflectResult*             p_result
+);
+
+/* @fn spvReflectGetOutputVariableByLocation
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  location  The "location" value of the requested output variable.
+                   A location of 0xFFFFFFFF will always return NULL
+                   with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an output interface variable
+                   with the provided location value, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableByLocation(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetOutputVariableByLocation")
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariable(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetEntryPointOutputVariableByLocation
+
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the output variable from.
+ @param  location     The "location" value of the requested output variable.
+                      A location of 0xFFFFFFFF will always return NULL
+                      with *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result     If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                      written to *p_result. Otherwise, a error code
+                      indicating the cause of the failure will be stored
+                      here.
+ @return              If the entry point contains an output interface variable
+                      with the provided location value, a pointer to that
+                      variable is returned. The caller must not free this
+                      pointer.
+                      If no match can be found, or if an unrelated error
+                      occurs, the return value will be NULL. Detailed
+                      error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableByLocation(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  uint32_t                       location,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetOutputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  semantic  The "semantic" value of the requested output variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the module contains an output interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetOutputVariableBySemantic(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    semantic,
+  SpvReflectResult*              p_result
+);
+
+/* @fn spvReflectGetEntryPointOutputVariableBySemantic
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the output variable from.
+ @param  semantic  The "semantic" value of the requested output variable.
+                   A semantic of NULL will return NULL.
+                   A semantic of "" will always return NULL with
+                   *p_result == ELEMENT_NOT_FOUND.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the entry point contains an output interface variable
+                   with the provided semantic, a pointer to that
+                   variable is returned. The caller must not free this
+                   pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+@note
+
+*/
+const SpvReflectInterfaceVariable* spvReflectGetEntryPointOutputVariableBySemantic(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  const char*                    semantic,
+  SpvReflectResult*              p_result
+);
+
+/*! @fn spvReflectGetPushConstantBlock
+
+ @param  p_module  Pointer to an instance of SpvReflectShaderModule.
+ @param  index     The index of the desired block within the module's
+                   array of push constant blocks.
+ @param  p_result  If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                   written to *p_result. Otherwise, a error code
+                   indicating the cause of the failure will be stored
+                   here.
+ @return           If the provided index is within range, a pointer to
+                   the corresponding push constant block is returned.
+                   The caller must not free this pointer.
+                   If no match can be found, or if an unrelated error
+                   occurs, the return value will be NULL. Detailed
+                   error results are written to *pResult.
+
+*/
+const SpvReflectBlockVariable* spvReflectGetPushConstantBlock(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       index,
+  SpvReflectResult*              p_result
+);
+SPV_REFLECT_DEPRECATED("renamed to spvReflectGetPushConstantBlock")
+const SpvReflectBlockVariable* spvReflectGetPushConstant(
+  const SpvReflectShaderModule*  p_module,
+  uint32_t                       index,
+  SpvReflectResult*              p_result
+);
+
+/*! @fn spvReflectGetEntryPointPushConstantBlock
+ @brief  Get the push constant block corresponding to the given entry point.
+         As by the Vulkan specification there can be no more than one push
+         constant block used by a given entry point, so if there is one it will
+         be returned, otherwise NULL will be returned.
+ @param  p_module     Pointer to an instance of SpvReflectShaderModule.
+ @param  entry_point  The entry point to get the push constant block from.
+ @param  p_result     If successful, SPV_REFLECT_RESULT_SUCCESS will be
+                      written to *p_result. Otherwise, a error code
+                      indicating the cause of the failure will be stored
+                      here.
+ @return              If the provided index is within range, a pointer to
+                      the corresponding push constant block is returned.
+                      The caller must not free this pointer.
+                      If no match can be found, or if an unrelated error
+                      occurs, the return value will be NULL. Detailed
+                      error results are written to *pResult.
+
+*/
+const SpvReflectBlockVariable* spvReflectGetEntryPointPushConstantBlock(
+  const SpvReflectShaderModule*  p_module,
+  const char*                    entry_point,
+  SpvReflectResult*              p_result
+);
+
+
+/*! @fn spvReflectChangeDescriptorBindingNumbers
+ @brief  Assign new set and/or binding numbers to a descriptor binding.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().  If the binding is used in multiple
+         entry points within the module, it will be changed in all of them.
+ @param  p_module            Pointer to an instance of SpvReflectShaderModule.
+ @param  p_binding           Pointer to the descriptor binding to modify.
+ @param  new_binding_number  The new binding number to assign to the
+                             provided descriptor binding.
+                             To leave the binding number unchanged, pass
+                             SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE.
+ @param  new_set_number      The new set number to assign to the
+                             provided descriptor binding. Successfully changing
+                             a descriptor binding's set number invalidates all
+                             existing SpvReflectDescriptorBinding and
+                             SpvReflectDescriptorSet pointers from this module.
+                             To leave the set number unchanged, pass
+                             SPV_REFLECT_SET_NUMBER_DONT_CHANGE.
+ @return                     If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                             Otherwise, the error code indicates the cause of
+                             the failure.
+*/
+SpvReflectResult spvReflectChangeDescriptorBindingNumbers(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           new_set_number
+);
+SPV_REFLECT_DEPRECATED("Renamed to spvReflectChangeDescriptorBindingNumbers")
+SpvReflectResult spvReflectChangeDescriptorBindingNumber(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectDescriptorBinding* p_descriptor_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           optional_new_set_number
+);
+
+/*! @fn spvReflectChangeDescriptorSetNumber
+ @brief  Assign a new set number to an entire descriptor set (including
+         all descriptor bindings in that set).
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().  If the descriptor set is used in
+         multiple entry points within the module, it will be modified in all
+         of them.
+ @param  p_module        Pointer to an instance of SpvReflectShaderModule.
+ @param  p_set           Pointer to the descriptor binding to modify.
+ @param  new_set_number  The new set number to assign to the
+                         provided descriptor set, and all its descriptor
+                         bindings. Successfully changing a descriptor
+                         binding's set number invalidates all existing
+                         SpvReflectDescriptorBinding and
+                         SpvReflectDescriptorSet pointers from this module.
+                         To leave the set number unchanged, pass
+                         SPV_REFLECT_SET_NUMBER_DONT_CHANGE.
+ @return                 If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                         Otherwise, the error code indicates the cause of
+                         the failure.
+*/
+SpvReflectResult spvReflectChangeDescriptorSetNumber(
+  SpvReflectShaderModule*        p_module,
+  const SpvReflectDescriptorSet* p_set,
+  uint32_t                       new_set_number
+);
+
+/*! @fn spvReflectChangeInputVariableLocation
+ @brief  Assign a new location to an input interface variable.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().
+         It is the caller's responsibility to avoid assigning the same
+         location to multiple input variables.  If the input variable is used
+         by multiple entry points in the module, it will be changed in all of
+         them.
+ @param  p_module          Pointer to an instance of SpvReflectShaderModule.
+ @param  p_input_variable  Pointer to the input variable to update.
+ @param  new_location      The new location to assign to p_input_variable.
+ @return                   If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                           Otherwise, the error code indicates the cause of
+                           the failure.
+
+*/
+SpvReflectResult spvReflectChangeInputVariableLocation(
+  SpvReflectShaderModule*            p_module,
+  const SpvReflectInterfaceVariable* p_input_variable,
+  uint32_t                           new_location
+);
+
+
+/*! @fn spvReflectChangeOutputVariableLocation
+ @brief  Assign a new location to an output interface variable.
+         In addition to updating the reflection data, this function modifies
+         the underlying SPIR-V bytecode. The updated code can be retrieved
+         with spvReflectGetCode().
+         It is the caller's responsibility to avoid assigning the same
+         location to multiple output variables.  If the output variable is used
+         by multiple entry points in the module, it will be changed in all of
+         them.
+ @param  p_module          Pointer to an instance of SpvReflectShaderModule.
+ @param  p_output_variable Pointer to the output variable to update.
+ @param  new_location      The new location to assign to p_output_variable.
+ @return                   If successful, returns SPV_REFLECT_RESULT_SUCCESS.
+                           Otherwise, the error code indicates the cause of
+                           the failure.
+
+*/
+SpvReflectResult spvReflectChangeOutputVariableLocation(
+  SpvReflectShaderModule*             p_module,
+  const SpvReflectInterfaceVariable*  p_output_variable,
+  uint32_t                            new_location
+);
+
+
+/*! @fn spvReflectSourceLanguage
+
+ @param  source_lang  The source language code.
+ @return Returns string of source language specified in \a source_lang.
+         The caller must not free the memory associated with this string.
+*/
+const char* spvReflectSourceLanguage(SpvSourceLanguage source_lang);
+
+/*! @fn spvReflectBlockVariableTypeName
+
+ @param  p_var Pointer to block variable.
+ @return Returns string of block variable's type description type name
+         or NULL if p_var is NULL.
+*/
+const char* spvReflectBlockVariableTypeName(
+  const SpvReflectBlockVariable* p_var
+);
+
+#if defined(__cplusplus)
+};
+#endif
+
+#if defined(__cplusplus)
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+namespace spv_reflect {
+
+/*! \class ShaderModule
+
+*/
+class ShaderModule {
+public:
+  ShaderModule();
+  ShaderModule(size_t size, const void* p_code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ShaderModule(const std::vector<uint8_t>& code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ShaderModule(const std::vector<uint32_t>& code, SpvReflectModuleFlags flags = SPV_REFLECT_MODULE_FLAG_NONE);
+  ~ShaderModule();
+
+  ShaderModule(ShaderModule&& other);
+  ShaderModule& operator=(ShaderModule&& other);
+
+  SpvReflectResult GetResult() const;
+
+  const SpvReflectShaderModule& GetShaderModule() const;
+
+  uint32_t        GetCodeSize() const;
+  const uint32_t* GetCode() const;
+
+  const char*           GetEntryPointName() const;
+
+  const char*           GetSourceFile() const;
+
+  uint32_t                      GetEntryPointCount() const;
+  const char*                   GetEntryPointName(uint32_t index) const;
+  SpvReflectShaderStageFlagBits GetEntryPointShaderStage(uint32_t index) const;
+
+  SpvReflectShaderStageFlagBits GetShaderStage() const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetShaderStage")
+  SpvReflectShaderStageFlagBits GetVulkanShaderStage() const {
+    return GetShaderStage();
+  }
+
+  SpvReflectResult  EnumerateDescriptorBindings(uint32_t* p_count, SpvReflectDescriptorBinding** pp_bindings) const;
+  SpvReflectResult  EnumerateEntryPointDescriptorBindings(const char* entry_point, uint32_t* p_count, SpvReflectDescriptorBinding** pp_bindings) const;
+  SpvReflectResult  EnumerateDescriptorSets( uint32_t* p_count, SpvReflectDescriptorSet** pp_sets) const ;
+  SpvReflectResult  EnumerateEntryPointDescriptorSets(const char* entry_point, uint32_t* p_count, SpvReflectDescriptorSet** pp_sets) const ;
+  SpvReflectResult  EnumerateInterfaceVariables(uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointInterfaceVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateInputVariables(uint32_t* p_count,SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointInputVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateOutputVariables(uint32_t* p_count,SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumerateEntryPointOutputVariables(const char* entry_point, uint32_t* p_count, SpvReflectInterfaceVariable** pp_variables) const;
+  SpvReflectResult  EnumeratePushConstantBlocks(uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const;
+  SpvReflectResult  EnumerateEntryPointPushConstantBlocks(const char* entry_point, uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const;
+  SPV_REFLECT_DEPRECATED("Renamed to EnumeratePushConstantBlocks")
+  SpvReflectResult  EnumeratePushConstants(uint32_t* p_count, SpvReflectBlockVariable** pp_blocks) const {
+    return EnumeratePushConstantBlocks(p_count, pp_blocks);
+  }
+
+  const SpvReflectDescriptorBinding*  GetDescriptorBinding(uint32_t binding_number, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorBinding*  GetEntryPointDescriptorBinding(const char* entry_point, uint32_t binding_number, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorSet*      GetDescriptorSet(uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectDescriptorSet*      GetEntryPointDescriptorSet(const char* entry_point, uint32_t set_number, SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetInputVariableByLocation(uint32_t location,  SpvReflectResult* p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetInputVariableByLocation")
+  const SpvReflectInterfaceVariable*  GetInputVariable(uint32_t location,  SpvReflectResult* p_result = nullptr) const {
+    return GetInputVariableByLocation(location, p_result);
+  }
+  const SpvReflectInterfaceVariable*  GetEntryPointInputVariableByLocation(const char* entry_point, uint32_t location,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetInputVariableBySemantic(const char* semantic,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetEntryPointInputVariableBySemantic(const char* entry_point, const char* semantic,  SpvReflectResult* p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetOutputVariableByLocation(uint32_t location, SpvReflectResult*  p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetOutputVariableByLocation")
+  const SpvReflectInterfaceVariable*  GetOutputVariable(uint32_t location, SpvReflectResult*  p_result = nullptr) const {
+    return GetOutputVariableByLocation(location, p_result);
+  }
+  const SpvReflectInterfaceVariable*  GetEntryPointOutputVariableByLocation(const char* entry_point, uint32_t location, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetOutputVariableBySemantic(const char* semantic, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectInterfaceVariable*  GetEntryPointOutputVariableBySemantic(const char* entry_point, const char* semantic, SpvReflectResult*  p_result = nullptr) const;
+  const SpvReflectBlockVariable*      GetPushConstantBlock(uint32_t index, SpvReflectResult*  p_result = nullptr) const;
+  SPV_REFLECT_DEPRECATED("Renamed to GetPushConstantBlock")
+  const SpvReflectBlockVariable*      GetPushConstant(uint32_t index, SpvReflectResult*  p_result = nullptr) const {
+    return GetPushConstantBlock(index, p_result);
+  }
+  const SpvReflectBlockVariable*      GetEntryPointPushConstantBlock(const char* entry_point, SpvReflectResult*  p_result = nullptr) const;
+
+  SpvReflectResult ChangeDescriptorBindingNumbers(const SpvReflectDescriptorBinding* p_binding,
+      uint32_t new_binding_number = SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE,
+      uint32_t optional_new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE);
+  SPV_REFLECT_DEPRECATED("Renamed to ChangeDescriptorBindingNumbers")
+  SpvReflectResult ChangeDescriptorBindingNumber(const SpvReflectDescriptorBinding* p_binding, uint32_t new_binding_number = SPV_REFLECT_BINDING_NUMBER_DONT_CHANGE,
+      uint32_t new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE) {
+    return ChangeDescriptorBindingNumbers(p_binding, new_binding_number, new_set_number);
+  }
+  SpvReflectResult ChangeDescriptorSetNumber(const SpvReflectDescriptorSet* p_set, uint32_t new_set_number = SPV_REFLECT_SET_NUMBER_DONT_CHANGE);
+  SpvReflectResult ChangeInputVariableLocation(const SpvReflectInterfaceVariable* p_input_variable, uint32_t new_location);
+  SpvReflectResult ChangeOutputVariableLocation(const SpvReflectInterfaceVariable* p_output_variable, uint32_t new_location);
+
+private:
+  // Make noncopyable
+  ShaderModule(const ShaderModule&);
+  ShaderModule& operator=(const ShaderModule&);
+
+private:
+  mutable SpvReflectResult  m_result = SPV_REFLECT_RESULT_NOT_READY;
+  SpvReflectShaderModule    m_module = {};
+};
+
+
+// =================================================================================================
+// ShaderModule
+// =================================================================================================
+
+/*! @fn ShaderModule
+
+*/
+inline ShaderModule::ShaderModule() {}
+
+
+/*! @fn ShaderModule
+
+  @param  size
+  @param  p_code
+
+*/
+inline ShaderModule::ShaderModule(size_t size, const void* p_code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    size,
+    p_code,
+    &m_module);
+}
+
+/*! @fn ShaderModule
+
+  @param  code
+  
+*/
+inline ShaderModule::ShaderModule(const std::vector<uint8_t>& code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    code.size(),
+    code.data(),
+    &m_module);
+}
+
+/*! @fn ShaderModule
+
+  @param  code
+  
+*/
+inline ShaderModule::ShaderModule(const std::vector<uint32_t>& code, SpvReflectModuleFlags flags) {
+  m_result = spvReflectCreateShaderModule2(
+    flags,
+    code.size() * sizeof(uint32_t),
+    code.data(),
+    &m_module);
+}
+
+/*! @fn  ~ShaderModule
+
+*/
+inline ShaderModule::~ShaderModule() {
+  spvReflectDestroyShaderModule(&m_module);
+}
+
+
+inline ShaderModule::ShaderModule(ShaderModule&& other)
+{
+    *this = std::move(other);
+}
+
+inline ShaderModule& ShaderModule::operator=(ShaderModule&& other)
+{
+    m_result = std::move(other.m_result);
+    m_module = std::move(other.m_module);
+
+    other.m_module = {};
+    return *this;
+}
+
+/*! @fn GetResult
+
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::GetResult() const {
+  return m_result;
+}
+
+
+/*! @fn GetShaderModule
+
+  @return
+
+*/
+inline const SpvReflectShaderModule& ShaderModule::GetShaderModule() const {
+  return m_module;
+}
+
+
+/*! @fn GetCodeSize
+
+  @return
+
+  */
+inline uint32_t ShaderModule::GetCodeSize() const {
+  return spvReflectGetCodeSize(&m_module);
+}
+
+
+/*! @fn GetCode
+
+  @return
+
+*/
+inline const uint32_t* ShaderModule::GetCode() const {
+  return spvReflectGetCode(&m_module);
+}
+
+
+/*! @fn GetEntryPoint
+
+  @return Returns entry point
+
+*/
+inline const char* ShaderModule::GetEntryPointName() const {
+  return this->GetEntryPointName(0);
+}
+
+/*! @fn GetEntryPoint
+
+  @return Returns entry point
+
+*/
+inline const char* ShaderModule::GetSourceFile() const {
+  return m_module.source_file;
+}
+
+/*! @fn GetEntryPointCount
+
+  @param
+  @return
+*/
+inline uint32_t ShaderModule::GetEntryPointCount() const {
+  return m_module.entry_point_count;
+}
+
+/*! @fn GetEntryPointName
+
+  @param index
+  @return
+*/
+inline const char* ShaderModule::GetEntryPointName(uint32_t index) const {
+  return m_module.entry_points[index].name;
+}
+
+/*! @fn GetEntryPointShaderStage
+
+  @param index
+  @return Returns the shader stage for the entry point at \b index
+*/
+inline SpvReflectShaderStageFlagBits ShaderModule::GetEntryPointShaderStage(uint32_t index) const {
+  return m_module.entry_points[index].shader_stage;
+}
+
+/*! @fn GetShaderStage
+
+  @return Returns shader stage for the first entry point
+
+*/
+inline SpvReflectShaderStageFlagBits ShaderModule::GetShaderStage() const {
+  return m_module.shader_stage;
+}
+
+/*! @fn EnumerateDescriptorBindings
+
+  @param  count
+  @param  p_binding_numbers
+  @param  pp_bindings
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateDescriptorBindings(
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+) const
+{
+  m_result = spvReflectEnumerateDescriptorBindings(
+    &m_module,
+    p_count,
+    pp_bindings);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointDescriptorBindings
+
+  @param  entry_point
+  @param  count
+  @param  pp_bindings
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointDescriptorBindings(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectDescriptorBinding** pp_bindings
+) const
+{
+  m_result = spvReflectEnumerateEntryPointDescriptorBindings(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_bindings);
+  return m_result;
+}
+
+
+/*! @fn EnumerateDescriptorSets
+
+  @param  count
+  @param  pp_sets
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateDescriptorSets(
+  uint32_t*                 p_count,
+  SpvReflectDescriptorSet** pp_sets
+) const
+{
+  m_result = spvReflectEnumerateDescriptorSets(
+    &m_module,
+    p_count,
+    pp_sets);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointDescriptorSets
+
+  @param  entry_point
+  @param  count
+  @param  pp_sets
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointDescriptorSets(
+  const char*               entry_point,
+  uint32_t*                 p_count,
+  SpvReflectDescriptorSet** pp_sets
+) const
+{
+  m_result = spvReflectEnumerateEntryPointDescriptorSets(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_sets);
+  return m_result;
+}
+
+
+/*! @fn EnumerateInterfaceVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateInterfaceVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateInterfaceVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointInterfaceVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointInterfaceVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointInterfaceVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumerateInputVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateInputVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateInputVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointInputVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointInputVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointInputVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumerateOutputVariables
+
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateOutputVariables(
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateOutputVariables(
+    &m_module,
+    p_count,
+    pp_variables);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointOutputVariables
+
+  @param  entry_point
+  @param  count
+  @param  pp_variables
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointOutputVariables(
+  const char*                   entry_point,
+  uint32_t*                     p_count,
+  SpvReflectInterfaceVariable** pp_variables
+) const
+{
+  m_result = spvReflectEnumerateEntryPointOutputVariables(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_variables);
+  return m_result;
+}
+
+
+/*! @fn EnumeratePushConstantBlocks
+
+  @param  count
+  @param  pp_blocks
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumeratePushConstantBlocks(
+  uint32_t*                 p_count,
+  SpvReflectBlockVariable** pp_blocks
+) const
+{
+  m_result = spvReflectEnumeratePushConstantBlocks(
+    &m_module,
+    p_count,
+    pp_blocks);
+  return m_result;
+}
+
+/*! @fn EnumerateEntryPointPushConstantBlocks
+
+  @param  entry_point
+  @param  count
+  @param  pp_blocks
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::EnumerateEntryPointPushConstantBlocks(
+  const char*               entry_point,
+  uint32_t*                 p_count,
+  SpvReflectBlockVariable** pp_blocks
+) const
+{
+  m_result = spvReflectEnumerateEntryPointPushConstantBlocks(
+      &m_module,
+      entry_point,
+      p_count,
+      pp_blocks);
+  return m_result;
+}
+
+
+/*! @fn GetDescriptorBinding
+
+  @param  binding_number
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorBinding* ShaderModule::GetDescriptorBinding(
+  uint32_t          binding_number,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetDescriptorBinding(
+    &m_module,
+    binding_number,
+    set_number,
+    p_result);
+}
+
+/*! @fn GetEntryPointDescriptorBinding
+
+  @param  entry_point
+  @param  binding_number
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorBinding* ShaderModule::GetEntryPointDescriptorBinding(
+  const char*       entry_point,
+  uint32_t          binding_number,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointDescriptorBinding(
+    &m_module,
+    entry_point,
+    binding_number,
+    set_number,
+    p_result);
+}
+
+
+/*! @fn GetDescriptorSet
+
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorSet* ShaderModule::GetDescriptorSet(
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetDescriptorSet(
+    &m_module,
+    set_number,
+    p_result);
+}
+
+/*! @fn GetEntryPointDescriptorSet
+
+  @param  entry_point
+  @param  set_number
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectDescriptorSet* ShaderModule::GetEntryPointDescriptorSet(
+  const char*       entry_point,
+  uint32_t          set_number,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointDescriptorSet(
+    &m_module,
+    entry_point,
+    set_number,
+    p_result);
+}
+
+
+/*! @fn GetInputVariable
+
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetInputVariableByLocation(
+  uint32_t          location,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetInputVariableByLocation(
+    &m_module,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetInputVariableBySemantic(
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetInputVariableBySemantic(
+    &m_module,
+    semantic,
+    p_result);
+}
+
+/*! @fn GetEntryPointInputVariable
+
+  @param  entry_point
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointInputVariableByLocation(
+  const char*       entry_point,
+  uint32_t          location,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointInputVariableByLocation(
+    &m_module,
+    entry_point,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointInputVariableBySemantic(
+  const char*       entry_point,
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointInputVariableBySemantic(
+    &m_module,
+    entry_point,
+    semantic,
+    p_result);
+}
+
+
+/*! @fn GetOutputVariable
+
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetOutputVariableByLocation(
+  uint32_t           location,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetOutputVariableByLocation(
+    &m_module,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetOutputVariableBySemantic(
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetOutputVariableBySemantic(&m_module,
+    semantic,
+    p_result);
+}
+
+/*! @fn GetEntryPointOutputVariable
+
+  @param  entry_point
+  @param  location
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointOutputVariableByLocation(
+  const char*        entry_point,
+  uint32_t           location,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetEntryPointOutputVariableByLocation(
+    &m_module,
+    entry_point,
+    location,
+    p_result);
+}
+inline const SpvReflectInterfaceVariable* ShaderModule::GetEntryPointOutputVariableBySemantic(
+  const char*       entry_point,
+  const char*       semantic,
+  SpvReflectResult* p_result
+) const
+{
+  return spvReflectGetEntryPointOutputVariableBySemantic(
+    &m_module,
+    entry_point,
+    semantic,
+    p_result);
+}
+
+
+/*! @fn GetPushConstant
+
+  @param  index
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectBlockVariable* ShaderModule::GetPushConstantBlock(
+  uint32_t           index,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetPushConstantBlock(
+    &m_module,
+    index,
+    p_result);
+}
+
+/*! @fn GetEntryPointPushConstant
+
+  @param  entry_point
+  @param  index
+  @param  p_result
+  @return
+
+*/
+inline const SpvReflectBlockVariable* ShaderModule::GetEntryPointPushConstantBlock(
+  const char*        entry_point,
+  SpvReflectResult*  p_result
+) const
+{
+  return spvReflectGetEntryPointPushConstantBlock(
+    &m_module,
+    entry_point,
+    p_result);
+}
+
+
+/*! @fn ChangeDescriptorBindingNumbers
+
+  @param  p_binding
+  @param  new_binding_number
+  @param  new_set_number
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeDescriptorBindingNumbers(
+  const SpvReflectDescriptorBinding* p_binding,
+  uint32_t                           new_binding_number,
+  uint32_t                           new_set_number
+)
+{
+  return spvReflectChangeDescriptorBindingNumbers(
+    &m_module,
+    p_binding,
+    new_binding_number,
+    new_set_number);
+}
+
+
+/*! @fn ChangeDescriptorSetNumber
+
+  @param  p_set
+  @param  new_set_number
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeDescriptorSetNumber(
+  const SpvReflectDescriptorSet* p_set,
+  uint32_t                       new_set_number
+)
+{
+  return spvReflectChangeDescriptorSetNumber(
+    &m_module,
+    p_set,
+    new_set_number);
+}
+
+
+/*! @fn ChangeInputVariableLocation
+
+  @param  p_input_variable
+  @param  new_location
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeInputVariableLocation(
+  const SpvReflectInterfaceVariable* p_input_variable,
+  uint32_t                           new_location)
+{
+  return spvReflectChangeInputVariableLocation(
+    &m_module,
+    p_input_variable,
+    new_location);
+}
+
+
+/*! @fn ChangeOutputVariableLocation
+
+  @param  p_input_variable
+  @param  new_location
+  @return
+
+*/
+inline SpvReflectResult ShaderModule::ChangeOutputVariableLocation(
+  const SpvReflectInterfaceVariable* p_output_variable,
+  uint32_t                           new_location)
+{
+  return spvReflectChangeOutputVariableLocation(
+    &m_module,
+    p_output_variable,
+    new_location);
+}
+
+} // namespace spv_reflect
+#endif // defined(__cplusplus)
+#endif // SPIRV_REFLECT_H
diff --git a/lib/Data/SpirV/Reflect/FFI.hs b/lib/Data/SpirV/Reflect/FFI.hs
new file mode 100644
--- /dev/null
+++ b/lib/Data/SpirV/Reflect/FFI.hs
@@ -0,0 +1,34 @@
+module Data.SpirV.Reflect.FFI
+  ( Module
+  , load
+  , loadBytes
+  ) where
+
+import Control.Exception (finally)
+import Control.Monad.IO.Class (MonadIO(..))
+import Data.ByteString (ByteString)
+import Data.ByteString qualified as ByteString
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
+import Data.SpirV.Reflect.FFI.Internal qualified as C
+import Foreign (allocaBytes, castPtr)
+
+import Data.SpirV.Reflect.Module (Module)
+
+load :: MonadIO io => FilePath -> io Module
+load path = liftIO $
+  ByteString.readFile path >>= loadBytes
+
+loadBytes :: MonadIO io => ByteString -> io Module
+loadBytes bytes = liftIO do
+  unsafeUseAsCStringLen bytes \(code, size) -> do
+    allocaBytes C.shaderModuleSize \smPtr -> do
+      res <- C.createShaderModule2
+        C.SpvReflectModuleFlagNoCopy
+        (fromIntegral size)
+        (castPtr code)
+        smPtr
+      case res of
+        C.SpvReflectResultSuccess ->
+          C.inflateModule smPtr `finally` C.destroyShaderModule smPtr
+        err ->
+          error $ "spvReflectCreateShaderModule2:" <> show err
diff --git a/lib/Data/SpirV/Reflect/FFI/Internal.chs b/lib/Data/SpirV/Reflect/FFI/Internal.chs
new file mode 100644
--- /dev/null
+++ b/lib/Data/SpirV/Reflect/FFI/Internal.chs
@@ -0,0 +1,494 @@
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Data.SpirV.Reflect.FFI.Internal where
+
+import Data.Coerce (Coercible, coerce)
+import Data.Text (Text)
+import Data.Text qualified as Text
+import Data.Vector (Vector)
+import Data.Vector qualified as Vector
+import Data.Word (Word32)
+import Foreign.C.String (CString)
+import Foreign.C.Types (CULong)
+import Foreign.Marshal.Utils (maybePeek)
+import Foreign.Ptr (Ptr, castPtr, plusPtr, nullPtr)
+import Foreign.Storable (peek)
+import GHC.Ptr qualified as GHC
+
+import Data.SpirV.Reflect.BlockVariable (BlockVariable)
+import Data.SpirV.Reflect.BlockVariable qualified as BlockVariable
+import Data.SpirV.Reflect.DescriptorBinding (DescriptorBinding)
+import Data.SpirV.Reflect.DescriptorBinding qualified as DescriptorBinding
+import Data.SpirV.Reflect.DescriptorSet (DescriptorSet)
+import Data.SpirV.Reflect.DescriptorSet qualified as DescriptorSet
+import Data.SpirV.Reflect.Enums qualified as Enums
+import Data.SpirV.Reflect.InterfaceVariable (InterfaceVariable)
+import Data.SpirV.Reflect.InterfaceVariable qualified as InterfaceVariable
+import Data.SpirV.Reflect.Module (Module)
+import Data.SpirV.Reflect.Module qualified as Module
+import Data.SpirV.Reflect.Traits qualified as Traits
+import Data.SpirV.Reflect.TypeDescription (TypeDescription)
+import Data.SpirV.Reflect.TypeDescription qualified as TypeDescription
+
+#include "spirv_reflect.h"
+
+-- * Loader interface
+
+{#pointer *SpvReflectShaderModule as ShaderModulePtr #}
+
+{#enum
+  SpvReflectResult as Result
+  {underscoreToCase}
+  deriving (Eq, Ord, Show)
+#}
+
+{#fun unsafe
+  spvReflectCreateShaderModule as createShaderModule
+  { id `CULong'
+  , `Ptr ()'
+  , `ShaderModulePtr'
+  } -> `Result'
+#}
+
+{#enum
+  SpvReflectModuleFlagBits as ModuleFlags
+  {underscoreToCase}
+  deriving (Eq, Ord, Show)
+#}
+
+{#fun unsafe
+  spvReflectCreateShaderModule2 as createShaderModule2
+  { `ModuleFlags'
+  , id `CULong'
+  , `Ptr ()'
+  , `ShaderModulePtr'
+  } -> `Result'
+#}
+
+{#fun unsafe
+  spvReflectDestroyShaderModule as destroyShaderModule
+  { `ShaderModulePtr'
+  } -> `()'
+#}
+
+-- TODO: inflateEntryPoints :: ShaderModulePtr -> IO [EntryPoint]
+
+-- * Module
+
+shaderModuleSize :: Int
+shaderModuleSize = {#sizeof SpvReflectShaderModule #}
+
+inflateModule :: ShaderModulePtr -> IO Module
+inflateModule smp = do
+  let sm = castPtr smp
+
+  generator <- inflateEnum $
+    {#get SpvReflectShaderModule->generator #} sm
+
+  entry_point_name <- inflateText $
+    {#get SpvReflectShaderModule->entry_point_name #} sm
+
+  entry_point_id <- inflateIntegral $
+    {#get SpvReflectShaderModule->entry_point_id #} sm
+
+  -- TODO: enums
+  source_language <- inflateIntegral $
+    {#get SpvReflectShaderModule->source_language #} sm
+
+  source_language_version <- inflateIntegral $
+    {#get SpvReflectShaderModule->source_language_version #} sm
+
+  -- XXX: Uses value(s) from first entry point
+
+  -- TODO: flags
+  spirv_execution_model <- inflateIntegral $
+    {#get SpvReflectShaderModule->spirv_execution_model #} sm
+
+  -- TODO: flags
+  shader_stage <- inflateIntegral $
+    {#get SpvReflectShaderModule->shader_stage #} sm
+
+  descriptor_bindings <-
+    inflateVector
+      ({#get SpvReflectShaderModule->descriptor_binding_count #} sm)
+      ({#get SpvReflectShaderModule->descriptor_bindings #} sm)
+      {#sizeof SpvReflectDescriptorBinding #}
+      inflateDescriptorBinding
+
+  descriptor_sets <-
+    inflateVector
+      ({#get SpvReflectShaderModule->descriptor_set_count #} sm)
+      ({#get SpvReflectShaderModule->descriptor_sets #} sm)
+      {#sizeof SpvReflectDescriptorSet #}
+      inflateDescriptorSet
+
+  inputPtr <- {#get SpvReflectShaderModule->input_variables #} sm
+  input_variables <-
+    if inputPtr == nullPtr then
+      pure mempty
+    else
+      inflateVector
+        ({#get SpvReflectShaderModule->input_variable_count #} sm)
+        (peek inputPtr)
+        {#sizeof SpvReflectInterfaceVariable #}
+        inflateInterfaceVariable
+
+  outputPtr <- {#get SpvReflectShaderModule->output_variables #} sm
+  output_variables <-
+    if outputPtr == nullPtr then
+      pure mempty
+    else
+      inflateVector
+        ({#get SpvReflectShaderModule->output_variable_count #} sm)
+        (peek outputPtr)
+        {#sizeof SpvReflectInterfaceVariable #}
+        inflateInterfaceVariable
+
+  push_constants <-
+    inflateVector
+      ({#get SpvReflectShaderModule->push_constant_block_count #} sm)
+      ({#get SpvReflectShaderModule->push_constant_blocks #} sm)
+      {#sizeof SpvReflectBlockVariable #}
+      inflateBlockVariable
+
+  pure Module.Module{..}
+
+inflateVector :: Integral i => IO i -> IO (Ptr p) -> Int -> (Ptr p -> IO a) -> IO (Vector a)
+inflateVector getCount getItems itemSize inflate = do
+  count <- getCount
+  itemsPtr <- getItems
+  Vector.generateM (fromIntegral count) \pos -> do
+    inflate $ itemsPtr `plusPtr` (itemSize * pos)
+
+{#pointer *SpvReflectDescriptorBinding as DescriptorBindingPtr #}
+
+inflateDescriptorBinding :: DescriptorBindingPtr -> IO DescriptorBinding
+inflateDescriptorBinding db = do
+  spirv_id <- fmap Just . inflateIntegral $
+    {#get SpvReflectDescriptorBinding->spirv_id #} db
+
+  name <- inflateText $
+    {#get SpvReflectDescriptorBinding->name #} db
+
+  binding <- inflateIntegral $
+    {#get SpvReflectDescriptorBinding->binding #} db
+
+  input_attachment_index <- inflateIntegral $
+    {#get SpvReflectDescriptorBinding->input_attachment_index #} db
+
+  set <- inflateIntegral $
+    {#get SpvReflectDescriptorBinding->set #} db
+
+  descriptor_type <- inflateEnum $
+    {#get SpvReflectDescriptorBinding->descriptor_type #} db
+
+  resource_type <- inflateFlags32 $
+    {#get SpvReflectDescriptorBinding->resource_type #} db
+
+  image <-
+    inflateImageTraits
+      db
+      {#offsetof SpvReflectDescriptorBinding->image #}
+
+  block <-
+    {#get SpvReflectDescriptorBinding->block #} db >>=
+      maybePeek inflateBlockVariable
+
+  array <-
+    inflateArrayTraits
+      db
+      {#offsetof SpvReflectDescriptorBinding->array #}
+
+  count <- fmap Just . inflateIntegral $
+    {#get SpvReflectDescriptorBinding->count #} db
+
+  accessed <- inflateIntegral $
+    {#get SpvReflectDescriptorBinding->accessed #} db
+
+  uav_counter_id <- inflateIntegral $
+    {#get SpvReflectDescriptorBinding->uav_counter_id #} db
+
+  uav_counter_binding <-
+    {#get SpvReflectDescriptorBinding->uav_counter_binding #} db >>=
+      maybePeek inflateDescriptorBinding
+
+  type_description <-
+    {#get SpvReflectDescriptorBinding->type_description #} db >>=
+      maybePeek inflateTypeDescription
+
+  let word_offset = DescriptorBinding.WordOffset{..}
+
+  decoration_flags <- inflateFlags32 $
+    {#get SpvReflectDescriptorBinding->decoration_flags #} db
+
+  pure DescriptorBinding.DescriptorBinding{..}
+
+{#pointer *SpvReflectBlockVariable as BlockVariablePtr #}
+
+inflateBlockVariable :: BlockVariablePtr -> IO BlockVariable
+inflateBlockVariable bv = do
+  spirv_id <- fmap Just . inflateIntegral $
+    {#get SpvReflectBlockVariable->spirv_id #} bv
+
+  name <- fmap Just . inflateText $
+    {#get SpvReflectBlockVariable->name #} bv
+
+  offset <- inflateIntegral $
+    {#get SpvReflectBlockVariable->offset #} bv
+
+  absolute_offset <- inflateIntegral $
+    {#get SpvReflectBlockVariable->absolute_offset #} bv
+
+  size <- inflateIntegral $
+    {#get SpvReflectBlockVariable->size #} bv
+
+  padded_size <- inflateIntegral $
+    {#get SpvReflectBlockVariable->padded_size #} bv
+
+  decorations <- inflateFlags32 $
+    {#get SpvReflectBlockVariable->decoration_flags #} bv
+
+  numeric <-
+    inflateNumericTraits
+      bv
+      {#offsetof SpvReflectBlockVariable->numeric #}
+
+  array <-
+    inflateArrayTraits
+      bv
+      {#offsetof SpvReflectBlockVariable->array #}
+
+  members <-
+    inflateVector
+      ({#get SpvReflectBlockVariable->member_count #} bv)
+      ({#get SpvReflectBlockVariable->members #} bv)
+      {#sizeof SpvReflectBlockVariable #}
+      inflateBlockVariable
+
+  type_description <-
+    {#get SpvReflectBlockVariable->members #} bv >>=
+      maybePeek inflateTypeDescription
+
+  pure BlockVariable.BlockVariable{..}
+
+{#pointer *SpvReflectTypeDescription as TypeDescriptionPtr #}
+
+inflateTypeDescription :: TypeDescriptionPtr -> IO TypeDescription
+inflateTypeDescription td = do
+  id_ <- fmap Just . inflateIntegral $
+    {#get SpvReflectTypeDescription->id #} td
+
+  op <- fmap Just . inflateEnum $
+    {#get SpvReflectTypeDescription->op #} td
+
+  type_name <- fmap Just . inflateText $
+    {#get SpvReflectTypeDescription->type_name #} td
+
+  struct_member_name <- fmap Just . inflateText $
+    {#get SpvReflectTypeDescription->struct_member_name #} td
+
+  storage_class <- inflateEnum $
+    {#get SpvReflectTypeDescription->storage_class #} td
+
+  type_flags <- inflateFlags32 $
+    {#get SpvReflectTypeDescription->type_flags #} td
+
+  numeric <-
+    inflateNumericTraits
+      td
+      {#offsetof SpvReflectTypeDescription->traits.numeric #}
+
+  image <-
+    inflateImageTraits
+      td
+      {#offsetof SpvReflectTypeDescription->traits.image #}
+
+  array <-
+    inflateArrayTraits
+      td
+      {#offsetof SpvReflectTypeDescription->traits.array #}
+
+  let traits = Just TypeDescription.Traits{..}
+
+  members <-
+    inflateVector
+      ({#get SpvReflectTypeDescription->member_count #} td)
+      ({#get SpvReflectTypeDescription->members #} td)
+      {#sizeof SpvReflectTypeDescription #}
+      inflateTypeDescription
+
+  pure TypeDescription.TypeDescription{id=id_, ..}
+
+{#pointer *SpvReflectDescriptorSet as DescriptorSetPtr #}
+
+inflateDescriptorSet :: DescriptorSetPtr -> IO DescriptorSet
+inflateDescriptorSet ds = do
+  set <- inflateIntegral $
+    {#get SpvReflectDescriptorSet->set #} ds
+
+  bindingsPtr <- {#get SpvReflectDescriptorSet->bindings #} ds
+  bindings <-
+    inflateVector
+      ({#get SpvReflectDescriptorSet->binding_count #} ds)
+      (peek bindingsPtr)
+      {#sizeof SpvReflectDescriptorBinding #}
+      inflateDescriptorBinding
+
+  pure DescriptorSet.DescriptorSet{..}
+
+{#pointer *SpvReflectInterfaceVariable as InterfaceVariablePtr #}
+
+inflateInterfaceVariable :: InterfaceVariablePtr -> IO InterfaceVariable
+inflateInterfaceVariable iv = do
+  spirv_id <- fmap Just . inflateIntegral $
+    {#get SpvReflectInterfaceVariable->spirv_id #} iv
+
+  name <- fmap Just . inflateText $
+    {#get SpvReflectInterfaceVariable->name #} iv
+
+  location <- inflateIntegral $
+    {#get SpvReflectInterfaceVariable->location #} iv
+
+  storage_class <- inflateEnum $
+    {#get SpvReflectInterfaceVariable->storage_class #} iv
+
+  semantic <- fmap Just . inflateText $
+    {#get SpvReflectInterfaceVariable->semantic #} iv
+
+  decoration_flags <- inflateFlags32 $
+    {#get SpvReflectInterfaceVariable->decoration_flags #} iv
+
+  built_in <- inflateEnum $
+    {#get SpvReflectInterfaceVariable->built_in #} iv
+
+  numeric <-
+    inflateNumericTraits
+      iv
+      {#offsetof SpvReflectInterfaceVariable->numeric #}
+
+  array <-
+    inflateArrayTraits
+      iv
+      {#offsetof SpvReflectInterfaceVariable->array #}
+
+  members <-
+    inflateVector
+      ({#get SpvReflectInterfaceVariable->member_count #} iv)
+      ({#get SpvReflectInterfaceVariable->members #} iv)
+      {#sizeof SpvReflectInterfaceVariable #}
+      inflateInterfaceVariable
+
+  format <- inflateEnum $
+    {#get SpvReflectInterfaceVariable->format #} iv
+
+  type_description <-
+    {#get SpvReflectInterfaceVariable->type_description #} iv >>=
+      maybePeek inflateTypeDescription
+
+  wo_location <- inflateIntegral $
+    {#get SpvReflectInterfaceVariable->word_offset.location #} iv
+  let word_offset = InterfaceVariable.WordOffset{location=wo_location}
+
+  pure InterfaceVariable.InterfaceVariable{..}
+
+-- * Traits
+
+{#pointer *SpvReflectImageTraits as ImageTraitsPtr #}
+
+inflateImageTraits :: Ptr struct -> Int -> IO Traits.Image
+inflateImageTraits src offset = do
+  let it = castPtr src `plusPtr` offset
+
+  dim <- inflateEnum $
+    {#get SpvReflectImageTraits->dim #} it
+
+  depth <- inflateIntegral $
+    {#get SpvReflectImageTraits->depth #} it
+
+  arrayed <- inflateIntegral $
+    {#get SpvReflectImageTraits->arrayed #} it
+
+  ms <- inflateIntegral $
+    {#get SpvReflectImageTraits->ms #} it
+
+  sampled <- inflateIntegral $
+    {#get SpvReflectImageTraits->sampled #} it
+
+  image_format <- inflateEnum $
+    {#get SpvReflectImageTraits->image_format #} it
+
+  pure Traits.Image{..}
+
+{#pointer *SpvReflectNumericTraits as NumericTraitsPtr #}
+
+inflateNumericTraits :: Ptr struct -> Int -> IO Traits.Numeric
+inflateNumericTraits src offset = do
+  let nt = castPtr src `plusPtr` offset
+
+  width <- inflateIntegral $
+    {#get SpvReflectNumericTraits.scalar.width #} nt
+  signedness <- inflateIntegral $
+    {#get SpvReflectNumericTraits.scalar.signedness #} nt
+  let scalar = Traits.Scalar{..}
+
+  component_count <- inflateIntegral $
+    {#get SpvReflectNumericTraits->vector.component_count #} nt
+  let vector = Traits.Vector{..}
+
+  column_count <- inflateIntegral $
+    {#get SpvReflectNumericTraits->matrix.column_count #} nt
+  row_count <- inflateIntegral $
+    {#get SpvReflectNumericTraits->matrix.row_count #} nt
+  stride <- inflateIntegral $
+    {#get SpvReflectNumericTraits->matrix.stride #} nt
+  let matrix = Traits.Matrix{..}
+
+  pure Traits.Numeric{..}
+
+{#pointer *SpvReflectArrayTraits as ArrayTraitsPtr #}
+
+inflateArrayTraits :: Ptr struct -> Int -> IO Traits.Array
+inflateArrayTraits src offset = do
+  let at = castPtr src `plusPtr` offset
+
+  dims_count <- inflateIntegral $
+    {#get SpvReflectArrayTraits->dims_count #} at
+
+  dims <- fmap Vector.convert $ inflateVector
+    ({#get SpvReflectArrayTraits->dims_count #} at)
+    ({#get SpvReflectArrayTraits->dims #} at)
+    {#sizeof uint32_t #}
+    (fmap fromIntegral . peek)
+
+  stride <- fmap Just . inflateIntegral $
+    {#get SpvReflectArrayTraits->stride #} at
+
+  pure Traits.Array{..}
+
+-- * Atomic types
+
+inflateIntegral :: (Integral a, Num b) => IO a -> IO b
+inflateIntegral getIntegral =
+  getIntegral >>=
+      pure . fromIntegral
+
+inflateEnum :: (Integral a, Enum b) => IO a -> IO b
+inflateEnum getEnum =
+  getEnum >>=
+      pure . toEnum . fromIntegral
+
+inflateFlags32 :: forall a b . (Integral a, Coercible Word32 b) => IO a -> IO b
+inflateFlags32 gitBits =
+  gitBits >>=
+      pure . coerce @Word32 @b . fromIntegral
+
+inflateText :: IO CString -> IO Text
+inflateText getPtr =
+  getPtr >>= \ptr ->
+    if nullPtr == ptr then
+      pure mempty
+    else
+      case ptr of
+        GHC.Ptr addr ->
+          pure $! Text.unpackCString# addr
diff --git a/spirv-reflect-ffi.cabal b/spirv-reflect-ffi.cabal
new file mode 100644
--- /dev/null
+++ b/spirv-reflect-ffi.cabal
@@ -0,0 +1,99 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           spirv-reflect-ffi
+version:        0.1
+synopsis:       C-library loader for spirv-reflect tool.
+category:       Graphics
+author:         IC Rainbow
+maintainer:     ic.rbow@gmail.com
+copyright:      2022 IC Rainbow
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    cbits/include/spirv/unified1/spirv.h
+    cbits/spirv_reflect.c
+    cbits/spirv_reflect.h
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://gitlab.com/dpwiz/spirv-reflect
+
+flag tests
+  manual: True
+  default: False
+
+library
+  exposed-modules:
+      Data.SpirV.Reflect.FFI
+      Data.SpirV.Reflect.FFI.Internal
+  other-modules:
+      Paths_spirv_reflect_ffi
+  hs-source-dirs:
+      lib
+  default-extensions:
+      ApplicativeDo
+      BlockArguments
+      DeriveGeneric
+      DerivingStrategies
+      DuplicateRecordFields
+      FlexibleContexts
+      GeneralizedNewtypeDeriving
+      ImportQualifiedPost
+      LambdaCase
+      OverloadedStrings
+      PatternSynonyms
+      RecordWildCards
+      StrictData
+      TypeApplications
+  include-dirs:
+      cbits
+  c-sources:
+      cbits/spirv_reflect.c
+  build-tools:
+      c2hs
+  build-depends:
+      base >=4.7 && <5
+    , bytestring
+    , spirv-reflect-types >=0.2
+    , text
+    , vector
+  default-language: Haskell2010
+
+test-suite spirv-reflect-yaml-test
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  other-modules:
+      Paths_spirv_reflect_ffi
+  hs-source-dirs:
+      test
+  default-extensions:
+      ApplicativeDo
+      BlockArguments
+      DeriveGeneric
+      DerivingStrategies
+      DuplicateRecordFields
+      FlexibleContexts
+      GeneralizedNewtypeDeriving
+      ImportQualifiedPost
+      LambdaCase
+      OverloadedStrings
+      PatternSynonyms
+      RecordWildCards
+      StrictData
+      TypeApplications
+  build-depends:
+      base >=4.7 && <5
+    , directory
+    , filepath
+    , spirv-reflect-ffi
+    , tasty
+    , tasty-hunit
+  if !flag(tests)
+    buildable: False
+  default-language: Haskell2010
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE BangPatterns #-}
+
+module Main where
+
+import Control.Monad (guard)
+import System.Directory (doesDirectoryExist, listDirectory)
+import System.FilePath (splitExtensions, takeFileName, (</>))
+import Test.Tasty (TestTree, defaultMain, testGroup)
+import Test.Tasty.HUnit (testCase)
+
+import Data.SpirV.Reflect.FFI qualified as FFI
+
+main :: IO ()
+main = discover >>= defaultMain
+
+discover :: IO TestTree
+discover = do
+  groupDirs <- listDirectory UPSTREAM_PATH >>= traverse \path -> do
+    let groupPath = UPSTREAM_PATH </> path
+    isDir <- doesDirectoryExist groupPath
+    if not isDir then
+      pure mempty
+    else do
+      groupContents <- listDirectory groupPath
+      let
+        files = do
+          filePath <- groupContents
+          case splitExtensions filePath of
+            (name, ".spv") ->
+              pure
+                ( takeFileName name
+                , groupPath </> filePath
+                )
+            _skip ->
+              mempty
+      pure (path, files)
+
+  let
+    groups = do
+      (name, yamls) <- groupDirs
+      guard $ not (null yamls)
+      pure $ testGroup name (map mkTest yamls)
+
+  pure $ testGroup "upstream" groups
+
+mkTest :: (String, FilePath) -> TestTree
+mkTest (name, file) =
+  testCase name do
+    -- putStrLn $ file <> " >>>>"
+    !_loaded <- FFI.load file
+    pure ()
+    -- print loaded
+    -- putStrLn $ file <> " <<<<"
+
+pattern UPSTREAM_PATH :: FilePath
+pattern UPSTREAM_PATH = "../SPIRV-Reflect/tests/"
